Logo

郎哥编程

优化百度新闻爬虫项目

2020-12-09 199

1、为Item数据项添加处理器

在百度新闻爬虫案例项目中,对爬取数据的处理都在parse()函数内进行,掌握了ItemLoader类的应用后,可以把数据的处理放在ItemLoader类的子类来实现。

在items.py所在目录内,新建newsitemload.py文件,该文件用于对爬虫爬取的数据进行处理。

newsitemload.py代码如下:

# 导入默认的处理器
from scrapy.loader.processors import TakeFirst,Compose
# 导入ItemLoader类
from scrapy.loader import ItemLoader
# 定义BaiduNewsLoader类,用于对数据的清洗
# BaiduNewsLoader类继承ItemLoader类
# 定义类型转换处理器
def tosting(value):
  if type(value) is list:
    if len(value) > 0:
      return value[0]
   
 
# 定义过滤非新闻条目的处理器
def filter(title):
  # 若是新闻类别,返回title
  if len(title) > 0:
    if title[0] == "(":
      return title
     # 若title字符数大于10,返回title   
     if len(title) > 10:
      return title
   
# 处理新闻类别名称和新闻条目标题
def combin(data):
  if type(data) is list:
    if len(data) == 2:
       return [data[0] + data[1]]
    else:
      return data
 
class BaiduNewsLoader(ItemLoader):
  
  # 定义数据项news_title输入处理器
  news_title_in =  Compose(tosting,filter)
  # 定义数据项news_title输出处理器
  news_title_out = Compose(combin)
  # 定义数据项news_link的输入处理器
  news_link_in = Compose(tosting)

BaiduNewsLoader类继承于ItemLoader类。ItemLoader类在scrapy.loader模块内,需要导入ItemLoader类。BaiduNewsLoader类使用了Scapy内置的默认处理器Compose,内置处理器在scrapy.loader.processors模块内,需要导入内置处理器Compose。

BaiduNewsLoader类定义了三个处理函数,分别是tosting()、filter()、combin(),用于对爬取的数据进行处理。

tosting(value)函数获取列表对象value的第一个元素,并返回该元素的值,元素的值为str类型。

filter(title)函数过滤非新闻条目,参数title是字符串。过滤条件是title的长度要大于10个字符,若title的长度小于10个字符,程序会确定这不是新闻条目的标题,函数不返回任何内容。再一个过滤条件是title的第一个字符是否是“(”,爬虫在爬取分类新闻条目时,会添加新闻类别名称到数据项,新闻类别名称使用“()”括起来。

combin (data)函数用于合并新闻类别名称和新闻条目标题,参数data是一个列表对象,data若包含两个字符串类型的元素,函数会把这两个元素合并为一个字符串,并返回一个新列表对象,新列表对象唯一的元素为合并后的字符串。

在BaiduNewsLoader类内部,定义了数据项news_title的输入和输出处理器。

news_title的输入处理器为Compose(tosting,filter)。当程序在爬虫的parse()方法内或其它定义的解析方法,调用add_xpath()、add_value()等方法时,与该数据项绑定的输入处理器会被调用。

Compose(tosting,filter)处理器的处理过程为:

(1)处理器处理的数据来自add_xpath()、add_value()等方法传入或提取的数据,数据类型是列表对象;

(2)处理器调用tosting()函数,提取列表对象的第一个元素,并返回该元素,用于后续函数的处理;

(3)处理器调用filter()函数,过滤非新闻条目。参数title为tosting()函数返回的数据, 若函数返回数据,该数据会被添加到数据项,否则该数据项会被忽略。

news_title的输出处理器为Compose(combin)。当程序在爬虫的parse()方法内或其它定义的解析方法,调用ItemLoader类的load_item()方法时,与Item所有数据项绑定的输出处理器会被调用。

Compose(combin)的处理过程为:

(1)处理器处理的数据来自被输入处理器处理过的数据,若程序没有为数据项提供输入处理器,Scrapy会调用默认的内置处理器Identity(),对数据不做任何处理返回原数据;

(2)处理器调用combin ()函数,对data列表对象的元素进行合并。若data列表对象包含两个字符串类型的元素,则将这两个元素合并为一个元素,将该元素放入到一个新的列表对象,并返回这个列表对象。

在BaiduNewsLoader类内部,也定义了数据项news_link_in的输入处理器。

2、  修改爬虫代码

项目添加了BaiduNewsLoader类,在爬虫SpiderNewsbaiduSpider类内部使用BaiduNewsLoader类来处理爬取的数据。

修改后的SpiderNewsbaiduSpider类完整代码如下:

import scrapy
# 导入scrapy选择器
from scrapy.selector import Selector
# 导入NewsbaiduItem
from newsbaidu.items import NewsbaiduItem
# 导入BaiduNewsLoader
from newsbaidu.newsitemload import BaiduNewsLoader
   
class SpiderNewsbaiduSpider(scrapy.Spider):
    name = 'spider_newsbaidu'
    allowed_domains = ['https://news.baidu.com']
   start_urls = ['https://news.baidu.com/']
 
   def parse(self, response):
      # 获取爬取下来的网页代码
      html = response.text
      # 处理新闻类别网页
      # 使用xpath表达式搜寻新闻类别超链接标签
      category_node = response.xpath("//div[@id='channel-all']/div/ul/li[position()>1]/a/@href").extract()
      for request_node in category_node:
        url = response.urljoin(request_node)
        request = scrapy.Request(url,callback=self.parse_category,dont_filter=True)
        yield request
      # 使用xpath表达式搜寻指定的a标签节点,节点以列表方式返回
      item_nodes = response.xpath("//a[contains(@mon,'ct=1')]").extract()
      # 遍历节点
      for item_node in item_nodes:
        # 实例化BaiduNewsLoader对象
        loader = BaiduNewsLoader(item=NewsbaiduItem(), selector=Selector(text=str(item_node)))
        # 提取并清洗news_title数据项
        print(loader.add_xpath("news_title","//text()"))
        # 提取并清洗news_link数据项
        loader.add_xpath("news_link","//@href")
        # 获取收集的news_title数据项的值
        value = loader.get_collected_values("news_title")
        # 若value不为None,调用yield语句返回Item数据项
        if value:
          yield loader.load_item()
 
    # 处理类别网页的回调方法
    def parse_category(self, response):
       html = response.text
       # 解析新闻类别名称
      category = response.xpath("//li[contains(@class,'current active')]/a/text()").extract()
      # 使用xpath表达式搜寻指定的a标签节点,节点以列表方式返回
      item_nodes = response.xpath("//a[contains(@mon,'col') and not(contains(@mon,'col=carouse'))]").extract()
      # 遍历节点
      for item_node in item_nodes:
        # 实例化BaiduNewsLoader对象
        loader = BaiduNewsLoader(item=NewsbaiduItem(), selector=Selector(text=str(item_node)))
        pre_category = ["(" + category[0] + ")"]
        # 添加新闻类别名称
        loader.add_value("news_title",pre_category)
        # 添加新闻条目标题
        loader.add_xpath("news_title","//text()")
        # 使用Selector选择器获取超超链接的文本
        loader.add_xpath("news_link","//@href")
        # 获取收集的news_title数据项的值
        value = loader.get_collected_values("news_title")
        # 若value不为None,调用yield语句返回Item数据项
        if value:
          yield loader.load_item()

代码导入了BaiduNewsLoader类,在循环处理item_nodes列表对象(该对象存储了response.xpath返回的数据项)的过程中,实例化BaiduNewsLoader对象loader,传入的参数为NewsbaiduItem类的实例对象和Selector类的实例对象,Selector实例对象的输入内容来自item_nodes列表对象的元素。

loader对象分别调用add_xpath方法收集数据到news_title和news_link数据项,数据收集完成后。loader对象调用get_collected_values方法获取已收集news_title数据项的值,若值不为None,loader对象调用load_item方法最终填充数据到news_title和news_link数据项,并使用yield语句返回填充后的NewsbaiduItem实例对象。

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

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

评论区

登录 后发表评论
暂无评论