爬虫之scrapy

时间:2023-03-09 13:15:08
爬虫之scrapy

爬虫之scrapy

一、项目简单流程

1、创建项目

scrapy startproject 项目名

2、创建Spider

cd 项目名

scrapy genspider 爬虫名 域名

class YokaSpider(scrapy.Spider):
name = 'yoka'
allowed_domains = ['www.yoka.com/fashion/']
start_urls = ['http://www.yoka.com/fashion/'] def parse(self,response):
pass

创建的Spider类需继承scrapy.Spider

name:爬虫名

allowed_domains:允许爬取的域名,不在域名下的请求链接会被过滤掉

start_urls:Spider启动时爬取的url列表,初始请求由它来定义

parse:默认情况下, start_urls里的链接请求完成下载后,返回的响应就会作为唯一的参数传递给这个函数。该方法负责解析返回的响应、提取数据、进一步生成要处理的请求

3、创建Item

Item是保存爬取数据的容器,使用方法和字典类似,不过多了额外的保护机制,可以避免拼写错误和定义字段错误

创建的Item类需继承scrapy.Item,并定义类型为scrapy.Field的字段

class YokadapeiItem(scrapy.Item):
text= scrapy.Field()
tags=scrapy.Field()

4、解析Response

5、使用Item

①实例化 item=YokadapeiItem()

②赋值item['text']=...  item['tags']=...

③yield item

6、后续Request

使用scrapy.Request方法

yield scrapy.Request(url=请求连接,callback=回调函数)

7、运行

scrapy crawl 爬虫名

8、保存到文件

scrapy crawl 爬虫名 -o 文件名.后缀名(json、csv、xml等)

9、使用Item Pipeline

Item生成后,会自动被送到Item Pipeline进行处理

Item Pipeline的作用

①清理HTML数据

②验证爬取数据,检查爬取字段

③查重并丢弃重复内容

④将爬取结果保存到数据库

实现Item Pipeline

①定义一个类并实现process_item()方法 ---必须返回包含数据的字典或Item对象,或抛出DropItem异常

该方法有两个参数,第一个是item,每次Spider生成的Item都会作为参数传过来,第二个是spider,就是Spider的实例

接下来,我们是实现一个Item Pipeline筛调长度大于25的title

from scrapy.exceptions import DropItem

class TestSPipeline(object):
def __init__(self):
self.limit=25
def process_item(self, item, spider):
#判断该字段是否存在
if item['title']:
if len(item['title']) > self.limit:
item['title']=item['title'][0:self.limit].rstrip() + '...'
return item
else:
return DropItem("Missing title")

②将处理后的item存入MySQL

创建MysqlPipeline类

import pymsql

class MysqlPipeline(object):
def __init__(self,mysql_host,mysql_port,mysql_db,mysql_user,mysql_password,mysql_table):
self.mysql_host=mysql_host
self.mysql_port=mysql_port
self.mysql_db=mysql_db
self.mysql_table=mysql_table
self.mysql_user=mysql_user
self.mysql_password=mysql_password #类方法,用classmethod标识,通过参数crawler拿到全局配置的每个配置信息
#再settings中我们可以定义MYSQL_HOST和MYSQL_PORT等来指定MySQL连接需要的地址和端口等信息
@classmethod
def from_crawler(cls,crawler):
return cls(
mysql_host=crawler.settings.get('MYSQL_HOST'),
mysql_port=crawler.settings.get('PORT'),
mysql_db=crawler.settings.get('MYSQL_DB'),
mysql_user=crawler.settings.get('MYSQL_USER'),
mysql_password=crawler.settings.get('MYSQL_PASSWORD'),
mysql_table=crawler.settings.get('MYSQL_TABLE'),
) def open_spider(self,spider):
self.conn = pymysql.connect(host=self.mysql_host,port=self.mysql_port,
user=self.mysql_user,password=self.mysql_password,
database=self.mysql_db,charset='utf8')
self.cur=self.conn.cursor() def process_item(self,item,spider):
dc=dict(item)
for v in dc.values():
sql="insert into %s (title) values ('%s')"%(self.mysql_table,v)
self.cur.execute(sql)
self.conn.commit()
return item def close_spider(self,spider):
self.conn.close()

在配置文件settings中

#数字越小越先被调用
ITEM_PIPELINES = {
'test_s.pipelines.TestSPipeline': 300,
'test_s.pipelines.MysqlPipeline':400,
}
MYSQL_HOST='localhost'
MYSQL_PORT='3306'
MYSQL_DB='scrapy'
MYSQL_TABLE='titles'
MYSQL_USER='root'
MYSQL_PASSWORD='123456'

二、Selector的用法

xpath、css、正则

三、Spider的用法

1、Spider运行流程 ---Spider主要做两件事[1]、定义爬取网站的动作 [2]、分析爬取下来的网页

爬虫之scrapy

2、Spider类 ---提供了start_requests()方法的默认实现 -> 读取start_urls属性,并根据返回的结果调用parse()方法解析结果

属性:

①name:爬虫名称

②allowed_domains:允许爬虫的域名

③start_urls:起始URL列表,当没有实现start_requests()时,默认会从这个列表开始抓取

③custom_settings:字典,专属本Spider的配置,覆盖项目全局的设置。此设置必须在初始化之前被更新,必须定义成类变量

④crawler:由from_crawler()方法设置的,代表本Spider类对应的Crawler对象,利用它可以获取项目的一些配置信息

⑤settings;Settings对象,可以直接获取项目的全局设置变量

方法:

①start_requests():生成初始请求,必须返回一个可迭代对象。默认使用start_urls里的URL来构造Request(GET请求方式),

若想以POST方式访问某个站点,可以直接重写该方法,发送POST请求时使用FormRequest

②parse():当Response没有指定回调函数时,该方法会默认被调用。该方法需返回一个包含Request或Item的可迭代对象

③closed():当Spider关闭时,该方法会被调用

四、Downloader Middleware的用法

Scrapy内置了许多Downloader Middleware,被定义在DOWNLOADER_MIDDLEWARES_BASE变量中,

可以在settings修改DOWNLOADER_MIDDLEWARES以及禁用(将该中间件优先级设置为None)内置的Downloader Middleware

在整个架构中的位置为:

①在Request执行下载之前对其进行修改

②在生成Response被Spider解析之前对其进行修改

可以修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等

核心方法有三个:process_request(request,spider)、process_response(request,response,spider)、process_exception(request,exception,spider)

实现至少一个方法,就可以定义一个Downloader Middleware

1、process_request(request,spider)

在Request从队列里调度出来到Downloader下载执行之前,都可以用process_request()方法对Request进行处理

参数:①request:Request对象,即被处理的Request

   ②spider:Spider对象,即此Request对应的Spider

---返回值必须为None、Response对象、Request对象 之一,或抛出IgnoreRequest异常

①返回None时,不同的Downloader Middleware按照设置的优先级顺序依次对Request进行修改,最后送至Downloader执行

②返回Response对象时,更低优先级的Downloader Middleware的process_request()和process_exception()不会继续调用,每个Downloader Middleware的process_response()依次被调用,调用完毕后,直接将Response对象发送给Spider来处理

③返回Request对象时,更低优先级的Downloader Middleware的process_request()停止执行,该Request会重新放到调度队列里,相当于全新的Request

④IgnoreRequest异常抛出,所有的Downloader Middleware的process_exception()依次执行,若没有一个方法处理该异常,那么Request的errorback()方法就会回调。如果该异常还没有被处理,那么它便会被忽略

2、process_response(request,response,spider)

参数:①request:Request对象,此Response对应的Request

   ②response:Response对象,此被处理的Response

   ③spider:Spider对象,此Response对应的Spider

不同的返回情况:

①返回Request对象时,更低优先级的Downloader Middleware的process_response()不会被调用,该Request会重新放到调度队列里,相当于全新的Request

②返回Response对象时,更低优先级的Downloader Middleware的process_response()继续调用,继续对该Response对象进行处理

③IgnoreRequest异常抛出,则Request的errorback()方法就会回调。如果该异常还没有被处理,那么它便会被忽略

3、process_exception(request,exception,spider)

参数:①request:Request对象,产生异常的Request

   ②exception:Exception对象,即抛出的异常

   ③spider:Spider对象,即Request对应的Spider

不同的返回情况:

①返回为None时,更低优先级的Downloader Middleware的process_exception()会被继续调用,直到所有的方法都被调度完毕

②返回Response对象时,更低优先级的Downloader Middleware的process_exception()不再继续调用,每个Downloader Middleware的process_response()转而被依次调用

③返回Request对象时,更低优先级的Downloader Middleware的process_exception()也不再继续调用,该Request会重新放到调度队列里,相当于全新的Request

4、实战:

在parse()中可以用 response.request.headers 查看请求头信息

如 b'User-Agent': [b'Scrapy/1.5.1 (+https://scrapy.org)'], 使用的Use-Agent是Scrapy/1.5.1 (+https://scrapy.org),它是由Scrapy内置的UserAgentMiddleware设置的

from scrapy import signals

class UserAgentMiddleware(object):
"""This middleware allows spiders to override the user_agent""" def __init__(self, user_agent='Scrapy'):
self.user_agent = user_agent @classmethod
def from_crawler(cls, crawler):
o = cls(crawler.settings['USER_AGENT'])
crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
return o def spider_opened(self, spider):
self.user_agent = getattr(spider, 'user_agent', self.user_agent) def process_request(self, request, spider):
if self.user_agent:
request.headers.setdefault(b'User-Agent', self.user_agent)

修改User-Agent有两种方式①直接修改在settings的USER_AGENT变量 ②通过Downloader Middleware的process_request()来修改

如需设置随机的User-Agent,需要使用第二种方法

class RandomUserAgentMiddleware(object):

    def __init__(self):
self.user_agents = [
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
"Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
"Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
"Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
] def process_request(self, request, spider):
request.headers['User-Agent']=random.choice(self.user_agents)

 Downloader Middleware组件非常重要,是做异常处理和应对反爬处理的核心。后面将用它来处理代理、Cookies等内容

五、Spider Middleware的用法

和Downloader Middleware一样,Scrapy内置了许多Spider Middleware,被定义在SPIDER_MIDDLEWARES_BASE变量中,

可以在settings修改SPIDER_MIDDLEWARES,会和SPIDER_MIDDLEWARES_BASE定义的Spider Middleware合并

在整个架构中的位置为:

①在Response发送给Spider之前对Response进行处理

②在Request发送给Scheduler之前对Request进行处理

③在Item发送给Item Pipeline之前对Item进行处理

核心方法有四个:process_spider_input(response,spider)、process_spider_output(response,result,spider)、process_spider_exception(response,exception,spider)、process_start_request(start_request,spider),实现至少一个方法,就可以定义一个Spider Middleware

1、process_spider_input(response,spider)

当Response被Spider Middleware处理时,process_spider_input()被调用

参数:①response:Response对象,即被处理的Response

   ②spider:Spider对象,即该Response对应的Spider

不同的返回情况:

①返回None时,Scrapy将会继续处理该Response,调用所有其他的Spider Middleware,直到Spider处理该Response

②抛出异常时,Scrapy将不会调用任何其他的Spider Middleware的process_spider_input(),而调用Request的errback(),errback的输出将重新输入到中间件中,使用process_spider_output()来处理,当其抛出异常时则调用process_spider_exception()来处理

2、process_spider_output(response,result,spider)

当Spider处理Response返回结果时,process_spider_output()被调用

参数:①response:Response对象,即生成该输出的Response

   ②result:包含Request或Item对象的可迭代对象,即Spider返回的结果

   ③spider:Spider对象,即其结果对应的Spider

   该方法必须返回包含Request或Item对象的可迭代对象。

3、process_spider_exception(response,exception,spider)

当Spider或Spider Middleware的process_spider_input()抛出异常时,process_spider_exception()被调用

参数:①response:Response对象,即异常被抛出时被处理的Response

   ②exception:Exception对象,即被抛出的异常

   ③spider:Spider对象,即抛出该异常的Spider

不同的返回情况:

①返回None时,Scrapy将会继续处理该异常,调用其他Spider Middleware中的process_spider_exception(),直到所有的Spider Middleware都被调用

②返回包含Request或Item对象的可迭代对象时,则其他Spider Middleware中的process_spider_output()被调用,其他的process_spider_exception()不会被调用

4、process_start_request(start_request,spider)

该方法以Spider启动的Request为参数时被调用,执行过程类似于process_spider_output(),不过没有相关联的Response,并且必须返回Request

参数:①start_request:包含Request的可迭代对象,即Start Request

   ②spider:Spider对象,即Start Request所属的Spider

   该方法必须返回另一个包含Request对象的可迭代对象

Spider Middleware的使用频率不如Downloader Middleware高,在必要的情况下它可以用来方便数据的处理

六、Item Pipeline的用法

当Spider解析完Response之后,Item就会传递到Item Pipeline,被定义的Item Pipeline组件会顺次调用。

Item Pipeline的作用:

    ①清理HTML数据

    ②验证爬取数据,检查爬取字段

    ③查重并丢弃重复内容

    ④将爬取结果保存到数据库

核心方法有四个,必须要实现process_item(item,spider),其他还有几个比较实用的方法,open_spider(spider)、close_spider(spider)、from_crawler(cls,crawler)

1、process_item(item,spider)

被定义的Item Pipeline会默认调用该方法对Item进行处理

参数:①item:Item对象,即被处理的Item

   ②spider:Spider对象,即生成该Item的Spider

不同的返回情况:

①返回Item对象时,此Item会被更低优先级的Item Pipeline的process_item()处理,直到所有的方法被调用完毕

②抛出DropItem异常,那么此Item会被丢弃,不再进行处理

2、open_spider(spider)

该方法实在Spider开启的时候被自动调用的。我们可以在这里做一些初始化操作,如开启数据库连接等

参数:spider:被开启的Spider对象

3、close_spider(spider)

该方法实在Spider关闭的时候被自动调用的。我们可以在这里做一些收尾工作,如关闭数据库连接等

参数:spider:被关闭的Spider对象

4、from_crawler(cls,crawler)

类方法,用@classmethod标识,是一种依赖注入的方式。

参数:①crawler:通过crawler对象,我们可以拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创建一个Pipeline实例

   ②cls:就是Class

   最后返回一个Class实例

七、通用爬虫

1、CrawlSpider

CrawlSpider继承Spider类,除了Spider类的所有属性和方法,还提供了非常重要的属性和方法

①rules:爬取规则的属性,包含一个或多个Rule对象的列表。每个Rule对爬取网站的动作都做了定义,CrawlSpider会读取rules的每一个Rule并进行解析

②parse_start_url():可重写的方法。当start_urls里对应的Request得到Response时,该方法被调用,它会分析Response并返回Item对象或者Request对象

最重要的是Rule的定义:

scrapy.contrib.spiders下的Rule类

class Rule(object):

    def __init__(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity):  

参数:

①link_extractor:Link Extractor对象,通过它提取链接并自动生成Request。它又是一个数据结构,常用LxmlLinkExtractor对象作为参数。

scrapy.linkextractors.lxmlhtml下的LxmlLinkExtractor类

class LxmlLinkExtractor(FilteringLinkExtractor):

    def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
tags=('a', 'area'), attrs=('href',), canonicalize=False,
unique=True, process_value=None, deny_extensions=None, restrict_css=(),
strip=True):

  [1]、allow是正则表达式或正则表达式列表,符合的链接才被跟进,deny则相反

  [2]、allow_domains是域名白名单,deny_domains则相反

  [3]、restrict_xpaths、restrict_css xpath和css表达式或其列表,用xpath或css提取链接

②callback:回调函数,注意,避免使用parse()作为回调函数

③cb_kwargs:字典,包含传递给回调函数的参数

④follow:布尔值,指定根据该规则从response提取的链接是否需要跟进。若callback为None,follow默认为True,否则默认为False

⑤process_links:指定处理函数,从link_extractor中获取链接列表时,该函数将会调用,主要用于过滤

⑥process_request:同样指定处理函数,根据该Rule提取到每个Request时,该函数都会调用,对Request进行处理。该函数必须返回Request或None

2、Item Loader

Item提供的是保存抓取数据的容器,Item Loader提供的是填充容器的机制。数据的提取会变得更加规则化。

scrapy.loader下的ItemLoader类

class ItemLoader(object):

    default_item_class = Item
default_input_processor = Identity()
default_output_processor = Identity()
default_selector_class = Selector def __init__(self, item=None, selector=None, response=None, parent=None, **context):

返回一个新的Item Loader来填充给定的Item,若没有给出Item,则使用中的类自动实例化default_item_class,用selector、response来使用选择器或响应参数实例化

参数:

①item:Item对象,可以调用add_xpath()、add_css()、add_value()等方法来填充Item对象

②selector:Selector对象,用来提取填充数据的选择器

③response:Response对象,用于使用构造选择器的Response

本文参考文献:[1]崔庆才.python3网络爬虫开发实战[M].北京:人民邮电出版社,2018:468-541.