说明:

今天主要学习一下翻页的功能,手动翻页的效果,前面的基础操作这里不不再依次讲解截图说明了,如果不太懂,可以参考我的上一篇scrapy博客:
https://blog.csdn.net/weixin_42081389/article/details/102390279

我的配置:

1
windows10系统
2
python3.6

目标网站:

http://lab.scrapyd.cn/
因为这个网站是get,响应的页面就能找到下一页的url,我记得之前测试翻页是用的腾讯招聘网站,但是现在腾讯招聘的页面改成异步获取的json数据了,如果真的爬取,我觉得直接使用requests模块比较方便,如果用scrapy,可以直接把开始的start_urls列表换成一个异步url列表集,我感觉使用scrapy那样爬取多此一举了。

比如这样,url列表集:

1
start_urls = ['https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1570587185160&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'.format(i) for i in range(1,101)]

不过这样的我不写,这样的和我的第一个博客没有什么区别,只不过初始化的url列表数量多了而已。

今天爬虫(手动提取url,发送get请求)

1、创建项目+初始化爬虫文件:

1
scrapy startpoject scrapyd_cn
2
cd scrapyd_cn
3
scapy genspider scrapyd lab.scrapyd.cn/

生成文件:

在这里插入图片描述
在这里插入图片描述

2、在setting中配置

1
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
2
ROBOTSTXT_OBEY = False
3
4
_LEVEL = 'DEBUG'
5
LOG_LEVEL = "WARNING"
6
7
ITEM_PIPELINES = {
8
   'scrapyd_cn.pipelines.ScrapydCnPipeline': 300,
9
}

3、修改items.py:

这里我们只要三维数据,需要三个字段即可。

在这里插入图片描述
在这里插入图片描述
1
import scrapy
2
3
4
class ScrapydCnItem(scrapy.Item):
5
    # define the fields for your item here like:
6
    text = scrapy.Field()
7
    author = scrapy.Field()
8
    url = scrapy.Field()
9
    # pass

4、修改爬虫程序:spiders/scrapyd.py

①、scrapy.Request()

这个只是和之前比这多了一个翻页功能,这个scrapy.Request()
里面有俩个必须要传递的参数,一个是url,一个是返回的函数,这里的parse是本身的方法中,继续处理数据直至页面结束,可以自己写多个方法,根据项目和网站需要定义方法,和需要的返回方法中。

1
# 翻页
2
            yield scrapy.Request(url=next_page, callback=self.parse)

其中还有几个常用的参数:

在这里插入图片描述
在这里插入图片描述

scrapy.Request()中几个常用参数解释:

1
url:必填:
2
callback:必填:请求之后返回到方法中处理然后数据
3
enthod:默认是get,如果是post需要自己手动更改,我这里是get,忽略了。
4
cookies:字典类型,有些网站需要cookies,可以携带上单个的cookies。
5
headers:字典类型,请求头,我的这个在setting里面加入了user-agent,这里不加也可以。
6
meta:这个很常用,这里是方法之间传递参数的
7
dont_filter:是否开启过滤,默认关闭,开启之后爬取过的url,下一次不会再爬取
8
errback:和callback类似,但是是处理对应的请求url报错时会进入errback,可以进入将报错的url打印出来或者单独保留下来,后续手动测试查找报错原因

②、直接上我的代码:

1
# -*- coding: utf-8 -*-
2
import scrapy
3
from scrapyd_cn.items import ScrapydCnItem
4
5
class ScrapydSpider(scrapy.Spider):
6
    name = 'scrapyd'
7
    allowed_domains = ['lab.scrapyd.cn']
8
    start_urls = ['http://lab.scrapyd.cn//']
9
10
    def parse(self, response):
11
        # 1、提取每一页的数据
12
        div_list = response.xpath('//*[@id="main"]/div[@class="quote post"]')
13
        for div in div_list:
14
            # extract_first() 和 get() 返回的结果是一样的。
15
            text = div.xpath('./span[@class="text"]/text()').get()
16
            # author = div.xpath('.//*[@class="author"]/text()').extract_first()
17
            author = div.xpath('.//*[@class="author"]/text()').get()
18
            url = div.xpath('.//a[contains(text(),"详情")]/@href').get()
19
            # print("div", text, author, url)
20
            item = ScrapydCnItem()
21
            item['text'] = text
22
            item['author'] = author
23
            item['url'] = url
24
            yield item
25
26
        # 2、下一页
27
        next_page = response.xpath('//*[@id="main"]//li[@class="next"]/a/@href').get()
28
        print("下一页",next_page)
29
        if next_page and len(next_page) > 5:
30
            # 翻页
31
            yield scrapy.Request(url=next_page, callback=self.parse)
32
        else:
33
            print("没有下一页了"*10)

5、管道处理(一般都在这里进行数据清洗和数据储存操作):pipelines.py

1、测试spider是什么:

下面是我测试的方法,之前使用过name属性进行过对不同的数据进行清洗和储存。你也可以自己研究测试。

1
class ScrapydCnPipeline(object):
2
    def process_item(self, item, spider):
3
        # print("pipelines.py",item,spider)
4
        # 1、测试spider是什么?,结果发现spdier就是我们爬虫程序,因为一个项目里面可以有多个爬虫程序,
5
        # print("pipelines.py",dir(spider)) # 打印出spider含有的属性和方法
6
        # ['allowed_domains', 'close', 'crawler', 'custom_settings', 'from_crawler', 'handles_request', 'log', 'logger', 'make_requests_from_url', 'name', 'parse', 'set_crawler', 'settings', 'start_requests', 'start_urls', 'update_settings']
7
        # print("pipelines.py", spider.name)
8
        # 发现我的爬虫程序的name是唯一的,如果一个项目有多个爬虫程序时,可以根据name进行分别进行建立不同的方法处理(比如不同的爬虫数据,需要存入不同的数据库中,或者需要的字段还不一致)
9
        # pipelines.py scrapyd
10
11
        # 2、处理返回的数据
12
13
14
        return item

2、保存到MongoDB数据库:

代码:

1
from pymongo import MongoClient
2
3
4
class ScrapydCnPipeline(object):
5
6
    def open_spider(self, spider):
7
        print("准备创建一个数据库")
8
        # 这个会在项目开始时第一次进入pipelines.py进入,之后不再进入
9
        # 建立于MongoClient 的连接:
10
        self.client = MongoClient('localhost', 27017)
11
        # 得到数据库
12
        self.db = self.client['111_test_database_scrapyd_cn']
13
        # 得到一个集合
14
        self.collection = self.db['111_test_collection_scrapyd_cn']
15
16
    def close_spider(self, spider):
17
        print('项目结束,断开数据库连接')
18
        # 这个会在结束时开始时第一次进入pipelines.py进入,之后不再进入
19
        self.client.close()
20
21
22
    def process_item(self, item, spider):
23
        # print("pipelines.py",item,spider)
24
        # 1、测试spider是什么?,结果发现spdier就是我们爬虫程序,因为一个项目里面可以有多个爬虫程序,
25
        # print("pipelines.py",dir(spider)) # 打印出spider含有的属性和方法
26
        # ['allowed_domains', 'close', 'crawler', 'custom_settings', 'from_crawler', 'handles_request', 'log', 'logger', 'make_requests_from_url', 'name', 'parse', 'set_crawler', 'settings', 'start_requests', 'start_urls', 'update_settings']
27
        # print("pipelines.py", spider.name)
28
        # 发现我的爬虫程序的name是唯一的,如果一个项目有多个爬虫程序时,可以根据name进行分别进行建立不同的方法处理(比如不同的爬虫数据,需要存入不同的数据库中,或者需要的字段还不一致)
29
        # pipelines.py scrapyd
30
31
        # 2、处理返回的数据
32
        # print("process_item", item, spider)
33
        # print("type", type(item))
34
        # 储存到数据库
35
        print("准备保存到数据库",item)
36
        self.collection.save(dict(item))
37
38
        return item

打印出的页面显示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数据MongoDB存入成功:

在这里插入图片描述
在这里插入图片描述

6、我刚学scrapy对itmes模块的一个疑问:

不知道刚学scrapy时有没有和我一样,有这样一个疑问,爬虫程序中,我不继承items中的ScrapydCnItem类,直接用一个字典代替,其实,我的理解,如果不涉及过的爬虫数据类型保存,是一样的,但是如果涉及过多的类型数据保存,会影响数据的混乱保存。比如进入管道中的,一个程序中,我就有好几种数据分别保存到不同的数据库,这时,继承items中的dict数据进入管道pipelines.py就可以用

isinstance(item,ScrapydCnItem)

返回的是True和False,进行保存自己需要的数据类型,到对应的需求数据库中。

在这里插入图片描述
在这里插入图片描述

scrapy.Item进入源文件是继承一个dict类:

在这里插入图片描述
在这里插入图片描述

scrapy.Field() 进入源文件也是继承一个dict

在这里插入图片描述
在这里插入图片描述

爬虫文件中,我测试发现定义一个字典返回的管道数据中储存,结果和定义的items的类ScrapydCnItem最后进入管道的结果是一样的,但是类型不一样,最后我找到一个可以理解这定义这个items中ScrapydCnItem类dict的原因:

1、我改为item={}

爬虫spider/scrapyd.py中,改为item = {}:

在这里插入图片描述
在这里插入图片描述

pipelines.py中打印到处item类型,和print(“isinstance”,isinstance(item,ScrapydCnItem))

其中这个ScrapydCnItem类,是items.py中的目标数据字段的类。
from scrapyd_cn.items import ScrapydCnItem

在这里插入图片描述
在这里插入图片描述

上面的打印结果:

在这里插入图片描述
在这里插入图片描述

2、我改为:item = ScrapydCnItem()

爬虫spider/scrapyd.py中

在这里插入图片描述
在这里插入图片描述

pipelines.py中,用来打印出我的疑问

在这里插入图片描述
在这里插入图片描述

打印出管道中的结果:

在这里插入图片描述
在这里插入图片描述

3、对比,得出我认为的结论:

通过上面俩个打印出的结论,我也就自己给出了我的疑问答案,那就是,items的文件,在爬虫程序中继承过来的字段类型,看着是字典,但是可以根据这个进行不同的数据类型(根据继续的items中的类来体现),根据isinstance(item,ScrapydCnItem),ScrapydCnItem要改为你实际需求的数据类名,进行不同数据类型分开保存。

这也就是我自己对这个items中的定义目标数据字段的理解。