Logo

郎哥编程

使用CrawlSpider类爬取百家号文章

2020-12-18 731

CrawlSpider类的功能比Spider类要强大很多,大多数爬虫程序都采用CrawlSpider类。在百度新闻爬虫项目中,增加一个CrawlSpider爬虫,用于专门爬取百家号的文章。

1、 如何爬取百家号的文章?

CrawlSpider爬虫将从百度新闻首页爬取,过滤掉所有非百家号文章的链接,下载百家号文章网页,从百家号文章网页提取文章内容存储到CSV文件。

具体开发过程遵循下面的步骤:

(1)使用Scrapy命令创建CrawlSpider爬虫;

(2)定义一个存储百家号文章的Item数据容器;

(3)定义数据处理ItemLoader类,用于去掉文章内容的HTML标签;

(4)定义Pipeline管道类,将爬取的数据存储到CSV文件;

(5)编辑CrawlSpider爬虫代码。

2、 创建CrawlSpider爬虫

创建CrawlSpider爬虫,使用下面的命令:

scrapy genspider -t crawl spider_bjhbaidu https://news.baidu.com

创建爬虫命令使用了命令选项-t,用于设置爬虫的模板,爬虫模板为crawl,spider_bjhbaidu是爬虫名称,https://news.baidu.com是该爬虫要爬取的网站域名。

设置项目所在目录为当前工作目录,在Windows命令行窗口输入下面的命令:

scrapy genspider -t crawl spider_bjhbaidu https://news.baidu.com

命令执行完成后,在项目的spiders目录下创建了模块文件spider_bjhbaidu.py,该模块文件就是CrawlSpider爬虫代码。

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class SpiderBjhbaiduSpider(CrawlSpider):
    name = 'spider_bjhbaidu'
    allowed_domains = ['https://news.baidu.com']
    start_urls = ['http://https://news.baidu.com/']
 
    rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )
 
    def parse_item(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

从代码中可以看出,SpiderBjhbaiduSpider继承了CrawlSpider类,Request请求响应默认处理的方法是parse_item(),该方法替换了parse()方法。CrawlSpider中不能再有以parse为名字的请求响应处理方法,这个方法被CrawlSpider用来实现基础url提取等功能。

代码中的rules定义了一个URL提取规则,Rule()方法返回一个规则。Rule()方法的第一个参数是LinkExtractor实例对象,它使用正则表达式和XPath表达式来过滤从网页中提取的URL链接;Rule()方法的第二个参数callback用来设置请求响应的回调方法,默认回调方法是parse_item;Rule()方法的第三个参数follow用于设置根据该规则从response提取的链接是否继续爬取。

在编写爬虫代码之前,需要先完成Item数据容器、数据处理类ItemLoader类、Pipeline管道类的定义。

3、 定义Item数据容器

百家号文章存储4个数据项(也称为字段),分别是作者、文章标题、文章内容、发布时间。

在百度新闻项目包目录下,新建article_item.py模块文件。代码如下:

import scrapy
# 定义结构化数据Article类
# Article类继承scrapy.Item类
class Article(scrapy.Item):
    # 定义title数据项,存储文章标题
    title = scrapy.Field()
    # 定义author数据项,存储文章作者
    author = scrapy.Field()
    # 定义content数据项,存储文章内容
    content = scrapy.Field()
    # 定义date数据项,存储文章发布时间
    date = scrapy.Field()

4、定义数据处理ItemLoader类

百家号文章数据爬取完成后,需要对数据进行处理(也称为清洗),过滤掉非必要的数据,提取需要的数据,清洗前的数据也称为脏数据。

在清洗任何脏数据之前,首先要理解脏数据的构成,才能确定要做哪些数据的清洗工作。ItemLoader类主要对百家号文章的4项数据进行清洗,分别是文章标题、文章作者、文章内容和文章发布时间。

文章标题数据的清洗

观察百家号文章网页的源代码,文章标题数据项构成如下:

<div>
  <h2>张大奕:新品两秒被抢空,28分钟卖货1个亿,却这么快就要转型</h2>
</div>

在爬虫类的解析方法中可以使用XPath表达式直接提取干净且完整的标题内容,该数据项无需进行清洗。

文章作者数据的清洗

观察百家号文章网页的源代码,文章作者数据项构成如下:

<p>瓶子说交易</p>

在爬虫类的解析方法中可以使用XPath表达式直接提取干净且完整的文章作者内容,该数据项无需进行清洗。

文章内容数据的清洗

观察百家号文章网页的源代码,文章内容为HTML数据,需要对文章内容进行清洗,过滤掉HTML标签。

文章发布时间数据的清洗

观察百家号文章网页的源代码,文章发布时间数据项构成如下:

<span>发布时间:10-06</span>

爬虫提取的发布时间不是标准时间格式,需要对提取的发布时间进行清洗,将时间转换为标准时间格式:XXXX-XX-XX(例如:2020-10-06)。

在百度新闻项目包目录下,新建article_itemload.py模块文件。代码如下:

# 导入时间处理类datetime
from datetime import datetime
# 导入默认的处理器
from scrapy.loader.processors import Compose
# 导入ItemLoader类
from scrapy.loader import ItemLoader
# 导入正则模块
import re
 
# 列表转换为字符串对象
def tosting(value):
    if type(value) is list:
        data = ""
        # 合并列表元素
        for item in value:
            data += item
        return data
   
 
# 去掉所有HTML标签,仅保留文本
def remove_tag(value):
    pattern = re.compile(r'<[^>]+>',re.S)
    result = pattern.sub('',value)
    return result
   
# 转换为标准时间格式XXXX-XX-XX
def convert_date(date):
    # 获取当前年份
    value = str(datetime.now().year) + "-"
    for ch in date:
        if ch.isdigit() or ch == '-':
            value+=ch
    return value
  
# 定义BaiJiaHaoLoader类,用于对数据的清洗
# BaiJiaHaoLoader类继承ItemLoader类
class BaiJiaHaoLoader(ItemLoader):
  
    # 定义数据项content输入处理器
    content_in =  Compose(tosting,remove_tag)
  
    # 定义数据项date的输入处理器
    date_in = Compose(tosting,convert_date)

tosting(value)函数将列表所有的元素合并为一个字符串对象。

remove_tag(value)函数使用正则表达式过滤掉value所有的HTML标签。

convert_date(date)函数将提取的时间转换为标准时间格式XXXX-XX-XX。

5、 定义Pipeline管道类

爬取的数据要存储到CSV文件,因此要定义一个负责存储爬取数据的Pipeline管道类。

在项目包目录下,新建pipelinesbjhcsv.py模块文件,代码如下:

# 导入csv模块
import csv
import os
 
class BjhPipeline:
   
    # 定义构造方法,打开CSV文件
    def __init__(self):
 
        # 定义CSV文件的存储路径
        store_file = os.path.dirname(__file__) + '/bjh.csv'
        # 以a+模式创建CSV文件
        self.file = open(store_file,'a+',newline='')
        # 实例化writer对象
        self.writer = csv.writer(self.file)
       
    def process_item(self, item, spider):
 
        # 获得item数据项适配器
        adapter = ItemAdapter(item)
        if adapter:
            # row为列表对象
            row = [adapter['title'][0],
                   adapter['author'][0],
                   adapter['content'][0],
                   adapter['date'][0]]
            # row写入CSV文件
            self.writer.writerow(row)
        return item
      
    # 爬虫程序关闭时,该方法被调用
    def close_spider(self,spider):
        # 关闭文件
        self.file.close()

6、 编辑CrawlSpider爬虫代码

最后剩下的工作就是编辑CrawlSpider爬虫代码,CrawlSpider爬虫在前面已经创建成功了,爬虫模块文件名称是spider_bjhbaidu.py。

完整的spider_bjhbaidu.py代码文件如下:

# 导入Item数据类Article
from newsbaidu.article_item import Article
# 导入scrapy选择器
from scrapy.selector import Selector
# 导入BaiduNewsLoader
from newsbaidu.article_itemload import BaiJiaHaoLoader
# 导入scrapy
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
 
class SpiderBjhbaiduSpider(CrawlSpider):
    name = 'spider_bjhbaidu'
    allowed_domains = ['https://news.baidu.com/','baijiahao.baidu.com']
    start_urls = ['https://news.baidu.com/']
    # 定义爬虫内置配置项,覆盖配置文件的配置项
    # 配置爬虫使用的管道类
    custom_settings = {
            'ITEM_PIPELINES':{
                'newsbaidu.pipelinesbjhcsv.BjhPipeline': 302,
            }
        }
    # 定义爬取规则,仅爬取包含baijiahao.baidu.com的链接
    rules = (
        Rule(LinkExtractor(allow=r'.*baijiahao.baidu.com.*'), callback='parse_item', follow=True),
    )
 
    def parse_item(self, response):
        # 获取爬取下来的网页代码
        html = response.text
        # 实例化BaiJiaHaoLoader对象
        loader = BaiJiaHaoLoader(item=Article(), selector=Selector(response=response))
        # 获取标题数据项
        loader.add_xpath("title","//div[@class='article-title']/h2/text()")
        # 获取文章作者数据项
        loader.add_xpath("author","//div[@class='author-txt']/p/text()")
        # 获取文章发布时间数据项
        loader.add_xpath("date","//span[@class='date']/text()")
        #获取文章内容
        content = response.xpath("//div[@class='article-content']/*").extract()
        loader.add_value("content",content)
        value = loader.get_collected_values("title")
        # 若value不为None,调用yield语句返回Item数据项
        # 过滤掉无内容的链接
        if value:
            yield loader.load_item()

BjhPipeline管道类并没有放置在项目的配置文件内,而是在爬虫内使用属性custom_settings进行配置,避免了Scrapy调用配置文件配置的管道类。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论