Scrapy-实战

Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。在之前的博文当中,简要介绍了Scrapy相关工具命令的介绍,以及Scrapy爬虫项目的基本结构。现在,我们需要自己编写一个Scrapy爬虫项目,从之前介绍Scrapy项目结构中可以知道,一个简单的爬虫,只需要对spideritems.py以及pipelines.py做一些相关操作即可,因为它们分别代表着爬虫部分相关的代码,爬虫项目的数据容器以及对爬虫数据的相关处理,这三个步骤对于爬虫项目是最为重要的!!!

Items的编写

使用Scrapy中的Item对象可以保存爬取到的数据,相当于存储爬取到的数据的容器。一般来说,互联网网页中的信息比较庞大,基本上都是非结构化信息,这样的非结构化信息不太利于我们对信息的管理,所以此时,我们可以定义自己所关注的结构化信息,然后从庞大的互联网信息体系中提取出我们关注的结构化信息,这样可以更利于我们对数据的管理,提取之后,这些数据信息需要一个存储的地方,此时,可以将提取到的结构化数据存储到Item对象中。

定义结构化数据信息的格式如下:

结构化数据名 = scrapy.Field()

所以,若要对结构化数据网页标题、网页地址等进行定义。我们打开之前创建的爬虫项目myfirstspider,可以将items.py的代码修改为如下:

import scrapy
class MyfirstspiderItem(scrapy.Item):
    title = scrapy.Field()
    url = scrapy.Field()

可以看到,要定义一个结构化数据,只需要将Scrapy下的Field类实例化即可。

我们可以通过Python Shell来实际使用以下Items,更深入地理解Items。进入Python Shell,将上面的Items代码写上,如下:

>>> import scrapy
>>> class MyFirstspiderItem(scrapy.Item):
        title = scrapy.Field()
        url = scrapy.Field()
    
>>> item = MyFirstspiderItem(title='测试', url='http://www.baidu.com')
>>> print(item)
{'title': '测试', 'url': 'http://www.baidu.com'}

我们实例化了MyFirstspiderItem类,并初始化了参数,并且将MyFirstspiderItem的实例化对象打印了出来。可以发现,对应的数据会以字典的形式存储,原数据项会转变为字典中的字段名,原数据项对应的值会转变为字典中相应字段名对应的值。

如果我们要单独取某个字段对应的值,可以通过:对象名['字段名']的方式实现。比如,想单独输出对象中的title字段对应的值,可以通过如下方式实现:

>>> item['title']
'测试'

切记,不可以使用.的方式获取字段的值,否则将引发错误,如下:

>>> item.title
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/xxx/PycharmProjects/demo/venv/lib/python3.7/site-packages/scrapy/item.py", line 92, in __getattr__
    raise AttributeError(f"Use item[{name!r}] to get field value")
AttributeError: Use item['title'] to get field value

如果你想要获取该对象中所有的字段名,可以通过如下方式:

>>> item.keys()
dict_keys(['title', 'url'])

如果想获取此时对象中的项目视图,可以通过如下方式:

>>> item.items()
ItemsView({'title': '测试', 'url': 'http://www.baidu.com'})

Item的复制:

>>> item.copy()
{'title': '测试', 'url': 'http://www.baidu.com'}

dict与item的转化

>>> dict_item = dict(item)
>>> type(dict_item)
<class 'dict'>
>>> item = MyFirstspiderItem(dict_item)
>>> type(item)
<class '__main__.MyFirstspiderItem'>

Spider的编写

Spider类是Scrapy中与爬虫相关的基类,所有的爬虫文件都必须继承该类(scrapy.Spider)。在一个爬虫项目中,爬虫文件是一个极其重要的部分,爬虫锁进行的爬取动作以及数据提取等操作都是在该文件中进行定义和编写的,通过爬虫文件,可以定义如何对站站进行相应的爬取。比如,可以在爬虫项目中通过genspider命令创建一个爬虫文件,然后对该爬虫文件进行相应的修改与编写。下面通过编辑器打开之前创建爬虫文件secondspider.py,默认代码如下所示:

class SecondspiderSpider(scrapy.Spider):
    name = 'secondspider'
    allowed_domains = ['baidu.com']
    start_urls = ['http://baidu.com/']

    def parse(self, response, **kwargs):
        pass

可以看到,首先在爬虫文件中需要导入scrapy模块,然后创建了一个爬虫类SecondspiderSpider,该类继承了scrapy.Spider基类。同时,name属性的值为secondspider,代表的是爬虫名称,所以此时爬虫名称为secondspider。allowed_domains属性代表的是允许爬取的域名,如果启动了OffsiteMiddleware,非允许的域名对应的网址则会自动过滤掉。start_urls属性代表的是爬取的起始网址,如果没有特别指定爬取的URL网址,则会从该属性中定义的网易开始进行爬取,在该属性中,我们可以定义多个起始网址。在这里,还拥有一个名为parse的方法,如果没有特别指定回调函数,该方法是处理Scrapy爬虫爬取到的网页响应的默认方法,通过该方法,可以对响应进行处理并返回处理后的数据,同时该方法也负责链接的跟进。

除了这些默认生成的属性和方法之外,在Scrapy的spider中海油一些常用的属性和方法,具体如下:

名称属性或方法含义
start_requests()方法该方法会默认读取start_urls属性中定义的网址,为每一个网址生成一个Request请求对象,并返回可迭代对象
make_requests_from_url(url)方法该方法会被start_requests()调用,该方法负责实现生成Request请求对象
closed(reason)方法关闭Spider时,该方法会被调用
log(message, level=logging.DEBUG, **kw)方法使用该方法可以实现在Spider中添加log
init()方法该方法主要负责爬虫的初始化,为构造函数

下面,我们将刚刚那个爬虫文件secondspider.py进行相应的修改,如下所示:

class SecondspiderSpider(scrapy.Spider):
    name = 'secondspider'
    allowed_domains = ['baidu.com']
    start_urls = ["https://www.baidu.com"]

    def parse(self, response, **kwargs):
        item = MyfirstspiderItem()
        item["title"] = response.xpath('/html/head/title/text()').extract()
        print(item["title"])

在此,除了导入scrapy模块之外,我们还导入了items.py中的MyfirstspiderItem类,也就是上面所写的Items。写好程序之后,使用如下命令运行该爬虫:

scrapy crawl secondspider --nolog

执行完毕,得到相应的爬取结果,如下所示:

['百度一下,你就知道']

可以看到,此时已经成功将这个网页中的标题提取出来并输出。上方的代码中,为什么定义了start_urls属性就默认定义了起始网址呢?我们是否可以用其他的变量表示起始网址呢?当然是可以的。start_urls属性是默认的设置起始网址属性,如果我们想用其他的变量(属性)来作为设置起始网址的属性,可以通过重写start_requests()方法来实现。下面,我们将刚刚那个爬虫文件secondspider.py进行修改,如下所示:

class SecondspiderSpider(scrapy.Spider):
    name = 'secondspider'
    start_urls = ["https://www.baidu.com"]
    my_urls = ["http://www.qq.com/"]
    
    def start_requests(self):
        for url in self.my_urls:
            yield self.make_requests_from_url(url)

    def parse(self, response, **kwargs):
        item = MyfirstspiderItem()
        item["title"] = response.xpath('/html/head/title/text()').extract()
        print(item["title"])

写好文件之后,我们重新运行下该爬虫,得到结果如下所示:

['腾讯首页']

可以看到,此时会爬取我们新属性my_urls中设置的网址的标题,在爬虫文件中,我们通过重写start_requests()方法,将起始网址设置为了my_urls属性中读取,并且使用for循环遍历,每一次循环读取my_urls属性中的一个网址,并调用Scrapy中默认的make_requests_from_url(url)方法来实现生产Request请求对象,将生成的请求对象通过yield返回。

如果我不想在代码中写死我想要爬取的网址,希望可以通过传参的方式来动态的爬取我想要爬取的网址,那么我们又该如何实现呢?首先,我们可以在爬虫文件中重写构造方法__init__(),在构造方法中设置一个变量用于接收用户在执行该爬虫文件时传递过来的参数,接收到参数后,就可以使用用户传进来的值了。然后,我们在运行时,我们只需要通过-a选项执行对应的参数名和参数值即可实现参数的传递,非常方便。下面,我们将刚刚那个爬虫文件secondspider.py进行修改,如下所示:

class SecondspiderSpider(scrapy.Spider):
    name = 'secondspider'
    start_urls = ["https://www.baidu.com"]
    
    def __init__(self,url):
        self.start_urls = ["%s"%url]

    def parse(self, response, **kwargs):
        item = MyfirstspiderItem()
        item["title"] = response.xpath('/html/head/title/text()').extract()
        print(item["title"])

修改好文件之后,运行如下命令:

scrapy crawl secondspider -a url='http://www.sina.cn' --nolog

而后运行该爬虫,得到结果如下所示:

['手机新浪网']

可以看到,在运行的时候,通过-a选项将url参数的值设置为了新浪的网页地址,执行时成功接收到该参数,并且提取除了传递进来的网址的标题信息。

如果我们需要通过传递参数的方式来爬取多个网址,应该怎么实现呢?首先我们说一下其中的一种思路,我们可以这么做,我们可以通过-a选项在形式上指定一个参数后,但是在该参数中包含多个实际参数的信息,但是设置好各实际参数信息之间的间隔符号,随后在Spider文件中通过str.split()将各真实参数的信息分割出来,这样就可以使用多个参数了。我们将上面爬虫文件修改如下:

class SecondspiderSpider(scrapy.Spider):
    name = 'secondspider'
    start_urls = ["https://www.baidu.com"]
    
    def __init__(self, url):
        url_list = url.split('|')
        self.start_urls = url_list
            
    def parse(self, response, **kwargs):
        item = MyfirstspiderItem()
        item["title"] = response.xpath('/html/head/title/text()').extract()
        print(item["title"])

通过代码分析得知,参数是通过|来进行分割成多个url。然后我们通过以下代码来运行以上程序:

scrapy crawl secondspider -a url='http://www.sina.cn|http://www.csdn.net' --nolog
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先一定要知道 Scrapy爬虫框架 对新手非常的不友好,或者从某些视频网站上跟着视频学或者说从培训机构里学几天技能掌握的,主要原因有以下两个方面。框架模块内容太多,虽然只是实现了一个简单的爬虫工作,但是实际上完成一个页面的抓取在框架里最少要走8个以上的步骤,但是这些步骤都是基本都是依靠配置文件完成的,没有丰富的爬虫经验,这些模块很多都不知道是做什么的,也不知道怎么配置。基于框架内进行数据抓取仅限于那些通用的网站抓取,你就简单理解为一个完成重复工作的机器人就行了。但是如果是那种反爬比较厉害的网站那就是另外一种情况了,完全是爬虫工程师和网站开发者的一个博弈了,所以这种情况不适合任何一种爬虫框架。对于那些想在工作中摸鱼的Python工程师来说就一定要使用爬虫框架,你会发现省不少力气而且效率真心非常高,不过一切都是在对框架熟练掌握的基础上、和对业务的深刻理解来说来说。但凡说 Scrapy 无用的基本上没有认真阅读过 Scrapy 的源码,对于 Scrapy框架 中的众多功能在搭建爬虫系统的时候能想到用几个?而且是基本是没有做过大型的爬虫系统项目的。咱们倒着推这个事,你就知道为什么要用Scrapy框架了。我之前的单位是国家互联网的新闻中心,做的项目中的有一项是复现863课题舆情监控系统中的内容,使用的方法是 Scrapy爬虫框架 结合 Django Web 搭建的数据采集系统,抓取的目标对象包括新闻、博客、论坛等等,其中每天目标检测网站成千上万,如果不用框架去做这种数据采集得累死。1.抓取的数据存哪里?单纯Scrapy爬虫脚本写好了执行抓取任务时数据保存到哪里?ES、Mongodb、MySQL?如果做过开发的人都知道存 Mongodb 中,那些说把数据存到 MySQL 的人基本上99%是从培训机构里出来的,因为大多数培训机构爬虫是不讲 Mongodb 的。通过业务计算逻辑把数据传输到生产 ES 环境中。2.几千、几万个爬虫脚本应该怎么管理?很多刚入行的人都觉得爬虫脚本难写,其实并不是这样。最难的是如何管理密密麻麻数不清的爬虫脚本,这里介绍Python如何处理这个事情。管理方式无非集中,Web管理环境、GUI管理环境、手动管理环境,不管是哪一种都需要一定的开发基础和管理思路。比较省事的用别人写好的Scrapy管理框架,比如Gerapy爬虫管理框架。如同这样web直接一键管理爬虫脚本,更多内容看上面的文章,这里就不重复了。3.Scrapy如何面对反爬的?跟大多数单线抓取某个网站解决其反爬一样,只要把反爬的逻辑设置好,比如最简单的更换代理IP,更换header,解析JS生成cookie访问等等,都可以在框架中设置配置文件。4.如何提高代码编写效率以及数据爬取效率?一定要阅读源码,一定要阅读源码,一定要阅读源码你才能知道这个框架里到底有什么内容可以用。5.基于Python各种框架开发的爬虫定制化项目。
Scrapy-Redis队列是指使用Scrapy-Redis库将Scrapy的队列替换为Redis数据库的一种方法。通过将队列存储在Redis中,可以实现分布式爬虫的功能。具体来说,Scrapy-Redis库使用Redis数据库来存储爬取的URL,并在多个爬虫节点之间共享这些URL。这样,不同的爬虫节点可以从Redis中获取URL并进行爬取,实现了分布式爬取的效果。\[2\] 在使用Scrapy-Redis时,需要安装相应的模块,包括redis、redis-py-cluster、scrapy-redis和scrapy-redis-cluster等模块。这些模块的版本需要满足Scrapy-Redis的要求,例如redis的版本需要是2.10.6,redis-py-cluster的版本需要是1.3.6,scrapy-redis的版本需要是0.6.8,scrapy-redis-cluster的版本需要是0.4。\[3\] 通过使用Scrapy-Redis队列,可以实现分布式爬虫的高效运行,提高爬取效率和并发能力。 #### 引用[.reference_title] - *1* *3* [Scrapy-Redis入门实战](https://blog.csdn.net/pengjunlee/article/details/89853550)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [scrapy-redis 更改队列和分布式爬虫](https://blog.csdn.net/qq_40279964/article/details/87903435)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值