CrawlSpider

CrawlSpider

  • 在上一个爬虫案例中,我们是自己在解析完整个页面后获取下一页的 url,然后重新发送一个请求,有时候我们想要这样做:只要满足某个条件的 url,都进行爬取。这时候我们就可以通过 CrawlSpider 来完成
  • CrawlSpider 继承自 Spider,不过是在之前的基础上增加新功能,可以定义爬取的 url 规则,以后 scrapy 碰到满足条件的 url 都进行爬取,而不用手动的 yield Request()

创建 CrawlSpider 爬虫

  • 之前创建的方式是通过 scrapy genspider [爬虫名字] [域名] 的方式创建的,如果想要创建 CrawlSpider 爬虫,要用下面命令创建
    scrapy genspider -t crawl [爬虫名字] [域名]

LinkExtractors 链接提取器

  • 使用 LinkExtractors 可以不用程序员自己提取想要的 url,然后发送请求,这些工作都可以交给 LinkExtractors,他会在所有爬的页面中找到满足规则的 url,实现自动爬取
  • 主要参数:
    • allow:允许的 url,所有满足这个正则表达式的 url 都会被提取
    • deny:禁止的 url,所有满足这个正则表达式的 url 都不会被提取
    • allow_domains:允许的域名,只有在这个里面指定的域名的 url 才会被提取
    • deny_domains:禁止的域名,所有在这个里面指定的域名的 url 都不会被提取
    • restrict_xpaths:严格的 xpath 和 allow 共同过滤链接

Rule 规则类

  • 定义爬虫的规则类,以下对这个类做一个简单的介绍
  • 主要参数:
    • link_extractor:一个 LinkExtractor 对象,用于定义爬取规则
    • callback:满足这个规则的 url,应该要执行哪个回调函数,因为 CrawlSpider 使用了 parse 作为回调函数,因此不要覆盖 parse 作为回调函数自己的回调函数
    • follow:指定根据该规则从 response 中提取的链接是否需要跟进
    • process_links:从 link_extractor 中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接

用 CrawlSpider 爬取 小程序社区

settings.py

...
# 下载延迟
DOWNLOAD_DELAY = random.randint(1,3)

...

DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 \
                    Safari/537.36'
}

...

# 为了 pipelines.py 能够运行,需要将 ITEM_PIPELINES 取消注释
ITEM_PIPELINES = {
    # 300 表示优先级,值越小,优先级越高
   'test_spider.pipelines.TestSpiderPipeline': 300,
}
...

xcxsq.py

import scrapy

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from test_spider.items import TestSpiderItem


# class XcxsqSpider(CrawlSpider):
#     name = 'xcxsq'
#     allowed_domains = ['www.wxapp-union.com']
#     start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
#     domains = 'http://www.wxapp-union.com/'
#
#     rules = (
#         # 这里不用 callback,因为这个是获取教程的每页的文章的 url,我们想要的是具体的每个文章的内容
#         # follow=True 表示碰到同样规则的 url 需要跟进,scrapy会在返回的response中验证是否还有符合
#         # 规则的条目,继续跳转发起请求抓取,周而复始
#         Rule(LinkExtractor(
#             allow=r'.+mod=list&catid=2&page=[1-10]'
#             # restrict_xpaths='//'
#         ), callback='parse_url', follow=True),
#         # 这里需要 callback,因为这个是获取每个文章的具体内容,需要通过回调获取文章的内容
#         # follow=False 表示碰到同样规则的 url 不跟进,因为文章内有推荐的,url 类似的文章,
#         # 可能会和其他页中的文章有重复,导致爬取的文章顺序更变
#         # Rule(LinkExtractor(
#         #     allow=r'.+article-.+\.html'
#         # ),callback='parse_detail',follow=False)
#     )
#
#     def parse_url(self,response):
#         urls = response.xpath('//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a/@href').getall()
#         for url in urls:
#             page_url = self.domains + url
#             print(page_url)
#             yield scrapy.Request(page_url, callback=self.parse_detail, dont_filter=True)
#
#     def parse_detail(self, response):
#         # item = {}
#         #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#         #item['name'] = response.xpath('//div[@id="name"]').get()
#         #item['description'] = response.xpath('//div[@id="description"]').get()
#         # return item
#         title = response.xpath('//div[@class="h hm cl"]/div[@class="cl"]/h1[@class="ph"]/text()').get()
#         author = response.xpath('//div[@class="avatar_right cl"]/div[@class="cl"]//a/text()').get()
#         blockquote = response.xpath('//div[@class="blockquote"]/p/text()').get()
#         # content = response.xpath('//div[@class="content_middle cl"]//td[@id="article_content"]/')
#         print(title)
#         print(author)
#         print(blockquote)

class XcxsqSpider(CrawlSpider):
    name = 'xcxsq'
    allowed_domains = ['www.wxapp-union.com']
    start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
    domains = 'http://www.wxapp-union.com/'

    rules = (
        # 这里不用 callback,因为这个是获取教程的每页的文章的 url,我们想要的是具体的每个文章的内容
        # follow=True 表示碰到同样规则的 url 需要跟进,scrapy会在返回的response中验证是否还有符合
        # 规则的条目,继续跳转发起请求抓取,周而复始
        Rule(LinkExtractor(
            allow=r'.+mod=list&catid=2&page=[1-10]'
        ), follow=True),
        # 这里需要 callback,因为这个是获取每个文章的具体内容,需要通过回调获取文章的内容
        # follow=False 表示碰到同样规则的 url 不跟进,因为文章内有推荐的,url 类似的文章,
        # 可能会和其他页中的文章有重复,导致爬取的文章顺序更变
        # 因为当前页有推荐文章,和想要爬取的文章会有重复,所以这里用 restrict_xpaths 限制选区而不是 allow
        Rule(LinkExtractor(
            restrict_xpaths='//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a'
        ),callback='parse_detail',follow=False)
    )

    # def parse_url(self,response):
    #     urls = response.xpath('//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a/@href').getall()
    #     for url in urls:
    #         page_url = self.domains + url
    #         print(page_url)
    #         yield scrapy.Request(page_url, callback=self.parse_detail, dont_filter=True)

    def parse_detail(self, response):
        # item = {}
        #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
        #item['name'] = response.xpath('//div[@id="name"]').get()
        #item['description'] = response.xpath('//div[@id="description"]').get()
        # return item
        title = response.xpath('//div[@class="h hm cl"]/div[@class="cl"]/h1[@class="ph"]/text()').get()
        author = response.xpath('//div[@class="avatar_right cl"]/div[@class="cl"]//a/text()').get()
        blockquote = response.xpath('//div[@class="blockquote"]/p/text()').get()
        artical_content = response.xpath('//td[@id="article_content"]//text()').getall()
        content = ''.join(artical_content).strip()
        item = TestSpiderItem(title=title, author=author, blockquote=blockquote, content=content)
        yield item

pipelines.py

# 用 scrapy.exporters.JsonItemExporter
# JsonItemExporter 是将 item 都放入一个列表中,暂存在内存中,在 finish 时一起写入,比较耗内存
# JsonLinesItemExporter 是将 item 的字典逐行写入,节约内存,并且不用 start 和 finish
import json

from scrapy.exporters import JsonItemExporter, JsonLinesItemExporter


class TestSpiderPipeline(object):

    # def __init__(self):
    #     # 以 bytes 格式写入
    #     self.fp = open('duanzi.json','wb')
    #     # self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding='utf8')
    #     self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf8')
    #     # self.exporter.start_exporting()
    #
    # def open_spider(self,spider):
    #     # with open('duanzi.json','w',encoding='utf8')
    #     print('spider is running!')
    #
    # def process_item(self, item, spider):
    #     self.exporter.export_item(item)
    #     # self.fp.write('\n')
    #     return item
    #
    # def close_spider(self,spider):
    #     # self.exporter.finish_exporting()
    #     self.fp.close()
    #     print('spider was closed!')

    def __init__(self):
        self.fp = open('xcx.json','wb')
        self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii=False, encoding='utf8')

    def open_spider(self, spider):
        print('spider is runing')

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

    def close_spider(self, spider):
        print('spider was closed')
        self.fp.close()

items.py

import scrapy


class TestSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 将 json 中的 key 在这里定义,然后在解析数据中导入
    title = scrapy.Field()
    author = scrapy.Field()
    blockquote = scrapy.Field()
    content = scrapy.Field()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值