Python之Scrapy框架(笔记一)

什么是框架:

        通用性性强的项目半成品

什么是scrapy?

        Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。

Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy有自己的数据提取机制。它们被称为选择器,因为它们“选择”HTML文档的某些部分 XPath 或 CSS 表达。

XPath 是一种在XML文档中选择节点的语言,也可以与HTML一起使用。 CSS 是用于将样式应用于HTML文档的语言。它定义选择器,将这些样式与特定的HTML元素相关联。

通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片

环境安装:

-mac or linux :pip install scrapy

-window系统:

  • pip install wheel
  • 下载twisted,下载地址为:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twited
  • 安装twisted:pip install Twisted-20.3.0-cp39-cp39-win_amd64.whl
  • pip install pywin32 (pywin32提供了很多windows的操作接口)
  • pip install scrapy

#测试在终端里录入scrapy指令,没有报错表示安装成功

工程步骤:        

  1. 创建scrapy工程

                终端输入(cmd): scrapy startproject  scrapy_demo(工程名)

      2.创建爬虫文件(爬取内容)mydomin 为spider文件名

                进入spider文件中:scrapy genspider mydomain www.mydomain.com

       3.运行爬虫文件           

                scrapy crawl spiderName

                scrapy crawl spiderName --nolog   :工程运行时去除日志(可以配置去掉)

python正则re:必须会  https://www.runoob.com/python/python-reg-expressions.html

项目工程文件分布:

  •  scrapy.cfg # 部署配置文件
  • tutorial/ #project的Python模块,您将从这里导入代码__初始值
  • items.py #项目项定义文件,定义爬取的名称来存入爬取到的值
  • middleware.py #项目中间件文件,
  • pipelines.py #项目管道文件
  • settings.py #项目设置文件
  • spider/ #爬虫目录,爬虫.py文件进入该目录下创建

spider文件夹:

###########  scrapy genspider first_spider www.xxx.com  

爬虫文件:

import scrapy
from shici_new.items import ShiciNewItem
class FirstSpiderSpider(scrapy.Spider):
    #爬虫文件名称:爬虫源文件的一个唯一标识
    name = 'first_spider'
    #允许访问的域名(通常注释掉)
    #allowed_domains = ['www.xxx.com']
    #起始的url
    start_urls = ['https://www.shicimingju.com/book/xiyouji.html']
    def parse(self, response):
        print("================"+response.url+"=======================")
        list_ul= response.xpath('//div[@class="book-mulu"]/ul/li')
        #获取拼接url
        start_ur="https://www.shicimingju.com"
        for li in list_ul:
            #获取url
            li_url=li.xpath("./a/@href").extract()[0]
            title_url = start_ur+li_url
            #获取title
            title_new = li.xpath("./a/text()").get()
            print(title_new+"========================")
            title_new.strip()
            item = ShiciNewItem()
            item["title_new"] = title_new
            #将item提交给context_parse方法获取内容
            yield scrapy.Request(url=title_url,callback=self.content_parse,meta={"item": item})
    #title url的数据获取并解析
    def content_parse(self,response):
        item = response.meta["item"]
        # print(item["title_new"]) 成功获取item
        title_content =response.xpath("//div[@class='chapter_content']//text()").extract()
        title_content="".join(title_content).strip()
        item["title_content"]=title_content
        yield item

常用语法:

        scrapy自带有xpath解析

例如:

li_url=li.xpath("./a/@href").extract()[0]

   解析出来的list_url 类似于:用标签selector标签包裹,其结果类似于列表对象class:scrapy.selector.SelectorList,它标识一个列表

[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]

因此要获取标签中的值:

        extract():获取结果,存入list中,如果是一个值可用【0】/.extract_first()

        getall()和get()方法

        re()提取:

response.css('title::text').re(r'Quotes.*')
['Quotes to Scrape']
response.css('title::text').re(r'Q\w+')
['Quotes']
response.css('title::text').re(r'(\w+) to (\w+)')
['Quotes', 'Scrape']

可以将默认返回值作为参数提供,以代替 None :

>>> response.xpath('//div[@id="not-exists"]/text()').get(default='not-found')
'not-found'

而不是使用例如 '@src' xpath可以使用 .attrib A的性质 Selector :

>>> [img.attrib['src'] for img in response.css('img')]
['image1_thumb.jpg',
 'image2_thumb.jpg',
 'image3_thumb.jpg',
 'image4_thumb.jpg',
 'image5_thumb.jpg']

作为捷径, .attrib 也可以直接在SelectorList上使用;它返回第一个匹配元素的属性:

>>> response.css('img').attrib['src']
'image1_thumb.jpg'

当只需要一个结果时(例如,按id选择,或在网页上选择唯一元素时),此选项最有用:

response.css('base').attrib['href']
'http://example.com/'
  • Css选择器

  • 要选择文本节点,请使用 ::text

  • 要选择属性值,请使用 ::attr(name) 在哪里? name 是要为其值的属性的名称

将选择器与正则表达式一起使用

Selector 也有 .re() 使用正则表达式提取数据的方法。但是,与使用不同 .xpath() 或 .css() 方法, .re() 返回字符串列表。

下面是一个用于从 HTML code 以上:

>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
['My image 1',
 'My image 2',
 'My image 3',
 'My image 4',
 'My image 5']
def re(self, regex, replace_entities=True):
        """
       ``regex``可以是已编译的正则表达式,也可以是将使用
``re.compile(regex)``编译为正则表达式。默认情况下,
字符实体引用将替换为其对应字符(除`&amp;``外)和`&lt;``)
。将“替换”实体传递为“假”将关闭这些实体替代品。
        """
        return extract_regex(regex, self.get(), replace_entities=replace_entities)

、、、re(regex, replace_entities=True)
'''默认情况下,字符实体引用替换为其相应的字符(除了 &amp; 和 &lt; )经过 
replace_entities 作为 False 关闭这些替换。'''

yield scrapy.Request():

 yield scrapy.Request(url=title_url,callback=self.content_parse,meta={"item": item})

      *****把新地址传回搜索引擎,然后找到对应的方法来处理该请求,如果要传递值则用meta进行传递。item=response.meta["item"]   来获取。

item.py:

        item对象结构的定义在项目录下的items.py文件中定义。以类的方式定义item对象的各个字段,这个类继承与scrapy.Item


class MyItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    url = scrapy.Field()
    title = scrapy.Field()


item = MyItem()
item['url'] = 'http://www.xxx.com'
item['title'] = '类目'

Field对象

可以看到,在声明Item时,声明字段使用的是Field对象。这个Field对象其实完全继承自Python的字典,并且没有做任何改动,所以在使用Field声明字段时,可以传入数据作为这个字段的元数据(metadata),上方的serializer=str其实就是一个指定序列化函数的元数据。

字段的元数据与字段的值之间没有必然的联系。如果我们直接查看Item对象,那么获取的是字段的值,如果使用.fields,获取的就是字段的元数据了

pipelines.py

>>项目管道的典型用途是:

  • 清理 HTML 数据

  • 验证抓取的数据(检查项目是否包含某些字段)

  • 检查重复项(并删除它们)

  • 将抓取的项目存储在数据库中

此外,它们还可以实现以下方法:

》》open_spiderselfspider)这个方法在蜘蛛打开时被调用。

        参数 :spider ( Spiderobject) – openspider

》》close_spiderselfspider)当蜘蛛关闭时调用此方法。

》》from_crawlercls ,crawl)

        具体:https://blog.youkuaiyun.com/xiaoyu_wu/article/details/102551523

如果存在,则调用此类方法以从Crawler. 它必须返回管道的新实例。Crawler 对象提供对所有 Scrapy 核心组件的访问,如设置和信号;这是管道访问它们并将其功能挂钩到 Scrapy 的一种方式。

        参数crawler ( Crawlerobject) – 使用这个管道的爬虫

from itemadapter import ItemAdapter
#保存文档
class ShiciNewPipeline:
    fp = None
    def open_spider(self,spider):
        print("开始爬虫。。。")
        self.fp = open("./西游记title.txt","w",encoding="utf-8")
    def process_item(self, item, spider):
        title_xiyou = item["title_new"]
        title_content = item["title_content"]
        self.fp.write(title_xiyou+"\n"+title_content+"\n")
        print(title_xiyou+"文档保存成功")
        return item
    def close_spider(self,spider):
        print("结束爬虫")
        self.fp.close()

 import pymysql
 #保存到mysql
 class MysqlPipeline:
         conn = None
         cursor = None
         def open_spider(self,spider):
             print("创建mysql驱动")
             self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='root',db='myspider',charset='utf8')
             print("创建成功")
         def process_item(self,item,spider):
              #获取sql游标
             self.cursor = self.conn.cursor()
             sql ="insert into myshici (title) values (%s);"
             try:
              tit = item["title_new"]
              tit.strip()
              self.cursor.execute(sql,tit)

              self.conn.commit()
             except Exception as e:
                 print(e)
                 print("失败")
                 self.conn.rollback()
             return item
         def close_spider(self,spider):
             self.cursor.close()
             self.conn.close()

pipelines.py文件中可以有多个管道类:

同时保存在多个平台上,比如本地,mysql,radis等

***********注意*****

        1.不要忘记在settings配置文件中添加配置(值越小优先级越高)

ITEM_PIPELINES = {

    'shici_new.pipelines.ShiciNewPipeline': 300,
    'shici_new.pipelines.MysqlPipeline' : 305,
}

        2.要return item ,如果有多个管道类则可以获取到item值进行处理储存,如果不返回,优先级低的管道类获取不到item

图片管道:

       1. 要继承scrapy中管道里的图片管道

       2. 要在setting配置中配置图片存储的路径

                                IMAGES_STORE='/imgs' :表示最终图片存储路径

        3.开启管道配置

#image管道类
from scrapy.pipelines.images import ImagesPipeline
import scrapy
class ImagePipline(ImagesPipeline):
    #将图片地址返回到请求,图片地址进行请求操作
    def get_media_requests(self, item, info):
        yield  scrapy.request(item['url'])
    #返回图片存储的路径和名字
    def file_path(self, request, response=None, info=None, *, item=None):
        file_name=request.url.split('/')[-1]
        return file_name
    #将item返回到下一个执行的管道
    def item_completed(self, results, item, info):
        return item

中间件:middlewares.py

下载器中间件是位于引擎和下载器之间的特定钩子,当它们从引擎传递到下载器时处理请求,以及从下载器传递到引擎的响应。

如果需要执行以下操作之一,请使用下载器中间件:

  • 在将请求发送给下载者之前处理该请求(即在Scrapy将请求发送到网站之前);

  • 变更在传递给spider之前收到响应;

  • 发送新的请求,而不是将收到的响应传递给spider;

  • 在不获取网页的情况下将响应传递给蜘蛛;

  • 悄悄地放弃一些请求。

主要作用:

  • 在Scrapy将请求发送到网站之前修改,处理请求,如:更换代理ip,header等
  • 在将响应传递给引擎之前处理收到的响应,如:响应失败重新请求,或将失败的做一定处理再返回给引擎
  • 忽略一些响应或者请求

默认下载中间件

scrapy内置了一些默认配置,这些是不允许被修改的,通常是_BASE结尾的设置,比如DOWNLOADER_MIDDLEWARES_BASE下载中间件的默认设置,如下

{
 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100, 
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300, 
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350, 
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400, 
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500, 
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550, 
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560, 
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580, 
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590, 
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600, 
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700, 
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750, 
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850, 
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900, 
}

scrapy就是按照上面数字从小到大依次执行的,比如执行完RobotsTxtMiddleware的process_request()方法后会继续执行下面HttpAuthMiddleware等process_request(),可以看作串联的形式依次过流水线

如果我们要添加自定义的下载中间件,需要在settings.py中激活DOWNLOADER_MIDDLEWARES。同时想取消默认的一些中间件,也可以设置为None。注意的是激活DOWNLOADER_MIDDLEWARES并不会覆盖DOWNLOADER_MIDDLEWARES_BASE,而是继续串联起来

OWNLOADER_MIDDLEWARES = {

'myproject.middlewares.CustomDownloaderMiddleware': 543, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,

}

各默认中间件的可以参考https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#built-in-downloader-middleware-reference

自定义下载中间件

在创建项目后,再项目文件夹中有一middlewares.py文件,里面自动生成了两个中间件示例或者说模板。我们如果要自定义中间件的话,可以在给的示例上修改,或者新建类实现方法,或者继承已有的中间件重写方法

以下是下载中间件可以实现的方法,在自定义中间件时,可以根据需求实现

1.process_request(self, request, spider)

当每个request通过下载中间件时,该方法被调用。process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest 。最常使用的是返回None

  • 如果其返回 None ,会将处理过后的request丢给中间件链中的下一个中间件的process_request()方法处理,直到丢到下载器,由下载器下载
  • 如果其返回 Response 对象,Scrapy将不会调用任何其他的 process_request() 或 process_exception() 方法,也不会丢到下载器下载;直接将其返回的response丢到中间件链的process_response()处理。可以通过scrapy.http.Response构建Response 
  • 如果其返回 Request 对象,Scrapy则停止调用process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。
  • 如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。

参数:
  request(Request 对象)–处理的request
  spider(Spider 对象)–该request对应的spider

2.process_response(self, request, response, spider)

当下载的response返回时,process_response()被调用,且 必须返回以下之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。

  • 如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
  • 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
  • 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

参数:
  request (Request 对象) – response所对应的request
  response (Response 对象) – 被处理的response
  spider (Spider 对象) – response所对应的spider

3.process_exception(self, request, exception, spider)

当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括IgnoreRequest异常)时,Scrapy调用 process_exception() 。process_exception() 应该返回以下之一: 返回 None 、 一个 Response 对象、或者一个 Request 对象。

  • 如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
  • 如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。
  • 如果其返回一个 Request 对象, 则返回的request将会被重新调用下载。这将停止中间件的 process_exception() 方法执行,就如返回一个response的那样。

参数:
  request (是 Request 对象) – 产生异常的request
  exception (Exception 对象) – 抛出的异常
  spider (Spider 对象) – request对应的spider

4.from_crawler(cls, crawler)

如果存在,则调用此类方法创建中间件实例Crawler。它必须返回一个新的中间件实例。Crawler对象提供对所有Scrapy核心组件的访问,如设置和信号; 它是中间件访问它们并将其功能挂钩到Scrapy的一种方式。

参数 :crawler(Crawlerobject)- 使用此中间件的爬网程序 

 开启自定义下载中间件

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
}

****不是每个中间件都需要开启

class MiddleproDownloaderMiddleware(object):
    #UA池  list
    # 设置ua池  user_agent_list = [‘。。。。’,‘....’]
    #设置http和https的代理 list
    #HTTP_Llist = [....]   
    #HTTPS_Llist = [....]  

    #拦截请求(process:过程)

    def process_request(self, request, spider):
        #UA伪装:封装一个ua池从中随机选取一个
        request.headers["User-Agent"] = random.choice(self.user_agent_list)                
        return None

    #拦截所有响应

    def process_response(self,request,response,spider):
    	return response

     #拦截所有异常的请求

     def process_exception(self,request,exception,spider):

     	#代理ip(请求被拒后更换ip)
      	if request.url.split(":")[0] == 'http':
           	request.meta['proxy'] ='http://'+ random.choice(self.http的代理池)
           else:
    		request.meta['proxy'] ='https://'+ random.choice(self.https的代理池)
     	return request

-拦截响应:篡改响应数据,响应对象,遇到post请求的ajax数据时

当全站爬取数据后,遇到ajax请求时,不能直接获取,需要通过拦截相应,

用selenium来获取数据并返回再进行处理

'''在spider中定义selenium,实例化一个浏览器对象'''
class WangyiSpider(scrapy.Spider):
    models_url=[] # 存储响应数据为ajax动态加载等的网址
    #实例化一个浏览器对象
    def __init__(self):
        self.bros = webdriver.Chrome(executable_path='/User/驱动文件的地址')

           ''''''''中间的获取到的响应在响应的方法中进行处理并返回获取到的值''''''''
    
    #爬虫完成关闭时执行
    def closed(self,spider):
        self.bros.quit()





    
'''  import scrapy.spiders.init  一定要导入 不然spider中不会有该属性
    1.通过selenium的page_source来获取完整网页html
  2.再通过HttpResponse 要实例化一个响应对象,(from scrapy.http import HtmlResponse)
       把完整的html响应给spider
   参数: HtmlResponse(url,body,encoding,request)
'''
 def process_response(self, request, response, spider):

        '''spider 爬虫类,在爬虫文件设置一个属性存储url,在这里可以直接获取
        spider.models_url :models_url是spider爬虫类中定义保存ajax请求的网址
        通过比较请求的url和存储的是否一样,是就进行seleium来获取,
        不是则直接返回response

        '''    
        #获取爬虫类中定义的浏览器对象
        bro = spider.bros

        if request.url in spider.models_url:
            bro.get(request.url)
            page_text = bro.page_source  #通过page_source属性来获取
            
            new_response = HtmlResponse(url=request.url,body=page_text,
                    encoding=utf-8,request=request)

            #将获取到的新的响应返回给spider
            return new_response
        else:

            return response #如果不是里边的则默认返回

=========================================================================

settings

  •    ROBOTSTXT_OBEY = False     #robot协议,true为遵守,false不遵守
  •   LOG_LEVEL="ERROR"            #执行爬虫时不打印日志文件
  • ITEM_PIPELINES = { 'shici_new.pipelines.ShiciNewPipeline': 300,}  # 开启管道进行数据存储
  • DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543,}   #开启下载中间件
  • COOKIES_ENABLED=false   #禁止使用cookies  规避被禁用
  • DOWNLOAD_DELAY = 4  # 有些网站设置了高频分析,访问频率过快会判定为自动爬取值为4 代表 4秒
  •  其余

=========================================================================

 scrapy命令:

  • scrapy startproject <project_name> [project_dir]:创建一个名为 project_name 下 project_dir 目录。如果 project_dir 没有指定, project_dir 将与 project_name .
    • 用法:
      $ scrapy startproject myproject
  • scrapy genspider [-t template] <name> <domain>:在当前文件夹或当前项目的 spiders 文件夹(如果从项目内部调用)。这个 <name> 参数设置为spider的 name ,同时 <domain> 用于生成 allowed_domains 和 start_urls 蜘蛛的属性。
    • 用法
      • $ scrapy genspider -l
        Available templates:
          basic
          crawl
          csvfeed
          xmlfeed
        
        $ scrapy genspider example example.com
        Created spider 'example' using template 'basic'
        
        $ scrapy genspider -t crawl scrapyorg scrapy.org
        Created spider 'scrapyorg' using template 'crawl'
  • scrapy view <url> :在浏览器中打开给定的URL,因为您的废蜘蛛会“看到”它。有时候蜘蛛看到的页面与普通用户不同,所以这可以用来检查蜘蛛“看到”什么,并确认它是你所期望的。
    • --spider=SPIDER :绕过Spider自动检测并强制使用特定Spider

    • --no-redirect :不遵循HTTP 3xx重定向(默认为遵循它们)

    • 用法:

      $ scrapy view http://www.example.com/some/page.html
      
             [ ... browser starts ... ]
  • scrapy runspider <spider_file.py> :行一个包含在python文件中的spider,而不必创建一个项目。
    • 用法:
      $ scrapy runspider myspider.py
              [ ... spider starts crawling ... ]
  •  scrapy settings [options]   :获取 Scrapy 设置的值。
    •    用法:
      $ scrapy settings --get BOT_NAME
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值