Scrapy爬虫框架6-深度爬虫

112 阅读5分钟

CrawlSpider介绍

  1. CrawlSpider是Scrapy框架中Spider爬⾍类的⼀个⼦类,除了继承到Spider的特性和功能外,还 有⾃⼰更加强⼤的规则爬取。

  2. CrawlSpider是通过定义爬取的规则,爬⾍通过规则可以⾃动爬取数据。

• CrawlSpider适⽤于需要⾃动跟进链接并提取数据的场景。

• 普通的爬⾍项⽬可能需要⼿动编写代码来实现链接的提取和访问,并且更加灵活和⾃定义。

• 选择使⽤哪种⽅式取决于你的具体需求和个⼈偏好。

CrawlSpider创建

  1. 创建scrapy工程:scrapy startproject 项目名
  2. 创建爬虫文件:scrapy genspider -t crawl 项目名 www.xxx.com (跟之前创建文件的指令多了一个“-t crawl”,表示创建的爬虫文件是基于CrawlSpider这个类)

rules使用

rules就是CrawlSpider类独有的功能,rules是Rule对象的集合,用于匹配符合要求的目标网址;

参数介绍:Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True)

  • 参数1:指定连接提取器,设置提取链接的规则(正则表达式)
  • 参数2:指定通过表达式解析到的链接的响应体返回函数(回调函数)
  • 参数3:是否将链接提取器继续作用到链接提取器取出的链接网页中

follow

follow是一个布尔值(bool),指定了根据该规则从response提取的链接是否需要跟进。

如果为True,那么在请求到的响应体数据中,会继续搜索满足条件的链接再次请求,会一直深入解析页面直到没有新的匹配链接。

LinkExtractor(链接提取器)

LxmlLinkExtractor是用于提取链接的类,它提供了各种参数用于配置连接提取的行为。下面是各个参数的详细介绍:

  • allow:一个正则表达式(或正则表达式的列表),仅匹配的URL会被提取

  • deny:一个正则表达式(或正则表达式的列表),匹配的URL会被排除

  • allow_domains:一个字符串(或者字符串的列表),仅提取这些域名下的链接

  • deny_domains:一个字符串(或者字符串的列表),忽略这些域名下的链接

  • restrict_xpaths:一个XPATH表达式(或XPATH表达式的列表),仅在XPATH表达式匹配的区域内提取链接

  • restrict_css:一个CSS选择器(或选择器的列表),仅在CSS选择器匹配的区域内提取链接

  • restrict_text:一个正则表达式(或正则表达式的列表),仅提供文本符合正则表达式的链接

  • tags:一个字符串(或字符串的列表),提取指定标签下的链接,默认为(“a”,“area”)。

  • attrs:一个字符串(或字符串的列表),提取指定属性下的链接,默认为(“href”)。

  • canonicalize:如果为True,则对URL进行规范化处理(去除多余斜杠/统一大小写/规范编码)。

  • unique:如果为True,则仅提取唯一的链接(去除相同的链接)。

  • process_value:一个函数,用于处理提取的链接值。

  • deny_extensions:一个字符串(或字符串的列表),忽略匹配这些扩展名的链接。

  • strip:如果为True,则去除链接中的空白字符。

以上参数,可以灵活配置LxmlLinkExtractor,以适应不同的链接提取需求。

深度案例:(豆瓣)

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class DoubanSpider(CrawlSpider):
    name = "douban"
    allowed_domains = ["movie.douban.com"]
    start_urls = ["https://movie.douban.com/top250"]


    rules = (
        # 翻页    ?这里会影响转译,所以需要换成\?来防止转译     但.不需要
        Rule(LinkExtractor("https://movie.douban.com/top250?start=\d+&filter=$"), callback=None, follow=True),
        # 详情页面获取,$是结尾的符号,follow=False 访问详情页面后,不再从详情页面中解析链接
        Rule(LinkExtractor('https://movie.douban.com/subject/\d+/$'), callback="parse_item", follow=False),
    )


    def parse_item(self, response):
        item = {"title":response.xpath('//h1/span[@property="v:itemreviewed"]/text()').get()}
        print(item["title"])
        return item

需要存储的话,pipelines.py需要编写一下,如下:

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class DoubanTopPipeline:
    def __init__(self):
        self.file = open('douban.text','w+',encoding='utf-8')


    def process_item(self, item, spider):
        self.file.write(f'{item}\n')
        return item

    def close_spider(self):
        self.file.close()

值得注意的是:这里爬取速度过快,可能会导致爬取数据不全的问题,可以通过更换代理id,cookies或者在setting.py里面设置一下爬取的速度(如下)等。

DOWNLOAD_DELAY = 0.5

Cookies设置

COOKIES_ENABLED:默认开启cookie,如果禁⽤,则不会发送任何cookie到Web服务器。

法1:在settings中设置cookie,在请求头中添加UA和cookie即可

DEFAULT_REQUEST_HEADERS = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
    'cookie': 'pgv_pvid=2232911505; pgv_info=ssid=s1787784160&pgvReferrer=; clientip=; paytoken=; '
}

**法2:通过中间件设置cookie,request.cookies=cookie_dict(需要将复制的cookie字符串转换成字典)

class CookieDownloaderMiddleware:
    def process_request(self, request, spider):
        cookie_str = 'pgv_pvid=2232911505;pgv_info=ssid=s1787784160&pgvReferrer=; clientip=; paytoken='
        # 字典推导式
        cookie_dict = {i.split('=')[0]: i.split('=')[1] for i in
        cookie_str.split('; ')}
        print(cookie_dict)
        request.cookies = cookie_dict

**法3:重写start_requests⽅法携带上cookie发送请求,构造请求并且yield(需要cookie字符串转成字典)

# 将字符串的cookie变成字典类型的cookie
    cookie_str = 'pgv_pvid=2232911505; pgv_info=ssid=s1787784160&pgvReferrer='
 # 字典推导式
    cookie_dict = {i.split('=')[0]: i.split('=')[1] for i in cookie_str.split('; ')}
    def start_requests(self):
        yield scrapy.Request(url=self.start_urls[0],
                            callback=self.parse,
                            cookies=self.cookie_dict
                )

POST请求

def start_requests(self):  #post请求
    yield scrapy.FormRequest(
        url=self.start_urls[0],
        callback=self.parse,
        formdata={},  # 携带请求头
    )

image.png