2025年1月6日 星期一 甲辰(龙)年 冬月初四 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

爬虫--Scrapy进阶(CrawlSpider翻页规则使用)

时间:08-16来源:作者:点击数:19
CDSY,CDSY.XYZ

Scrapy Shell

Scrapy终端是一个交互终端,我们可以在未启动spider的情况下尝试及调试代码

启动Scrapy Shell

  • scrapy shell "https://hr.tencent.com/position.php?&start=0#a"

Selectors选择器

  • Scrapy Selectors 内置 XPathCSS Selector 表达式机制
  • Selector有四个基本的方法,最常用的还是xpath:
  • xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
  • extract(): 序列化该节点为Unicode字符串并返回listextract_first()/get()
  • css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同 BeautifulSoup4
  • re(): 根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
  • # 使用xpath
  • response.xpath('//title')

Spider类

  • Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如
  • 何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是你定义爬取的动作及
  • 分析某个网页(或者是有些网页)的地方。
  • scrapy.Spider是最基本的类,所有编写的爬虫必须继承这个类。
  • 主要用到的函数及调用顺序为:
  • __init__():
  • 初始化爬虫名字和start_urls列表
  • start_requests()
  • 调用make_requests_from_url():生成Requests对象交给Scrapy下载并返回response
  • parse(self, response):
  • 解析response,并返回ItemRequests(需指定回调函数)。
  • Item传给Item pipline持久化,而Requests交由Scrapy下载,并由指定的回调函数处理
  • (默认parse()),一直进行循环,直到处理完所有的数据为止。

Spider类源码参考

  • #所有爬虫的基类,用户定义的爬虫必须从这个类继承
  • class Spider(object_ref):
  • # 定义spider名字的字符串(string)。
  • # spider的名字定义了Scrapy如何定位(并初始化)spider,所以其必须是唯一的。
  • # name是spider最重要的属性,而且是必须的。
  • # 一般做法是以该网站(domain)(不加后缀 )来命名spider。 例如,如果spider爬取 mywebsite.com ,该spider通常会被命名为 mywebsite
  • name = None
  • # 初始化,提取爬虫名字,start_urls
  • def __init__(self, name=None, **kwargs):
  • if name is not None:
  • self.name = name
  • # 如果爬虫没有名字,中断后续操作则报错
  • elif not getattr(self, 'name', None):
  • raise ValueError("%s must have a name" % type(self).__name__)
  • # python 对象或类型通过内置成员__dict__来存储成员信息
  • self.__dict__.update(kwargs)
  • #URL列表。当没有指定的URL时,spider将从该列表中开始进行爬取。 因此,第一个被获取到的页面的URL将是该列表之一。 后续的URL将会从获取到的数据中提取。
  • if not hasattr(self, 'start_urls'):
  • self.start_urls = []
  • # 打印Scrapy执行后的log信息
  • def log(self, message, level=log.DEBUG, **kw):
  • log.msg(message, spider=self, level=level, **kw)
  • # 判断对象object的属性是否存在,不存在则断言处理
  • def set_crawler(self, crawler):
  • assert not hasattr(self, '_crawler'), "Spider already bounded to %s" % crawler
  • self._crawler = crawler
  • @property
  • def crawler(self):
  • assert hasattr(self, '_crawler'), "Spider not bounded to any crawler"
  • return self._crawler
  • @property
  • def settings(self):
  • return self.crawler.settings
  • #该方法将读取start_urls内的地址,并为每一个地址生成一个Request对象,交给Scrapy下载并返回Response
  • #该方法仅调用一次
  • def start_requests(self):
  • for url in self.start_urls:
  • yield self.make_requests_from_url(url)
  • #start_requests()中调用,实际生成Request的函数。
  • #Request对象默认的回调函数为parse(),提交的方式为get
  • def make_requests_from_url(self, url):
  • return Request(url, dont_filter=True)
  • #默认的Request对象回调函数,处理返回的response。
  • #生成Item或者Request对象。用户必须实现这个
  • def parse(self, response):
  • raise NotImplementedError
  • @classmethod
  • def handles_request(cls, request):
  • return url_is_from_spider(request.url, cls)
  • def __str__(self):
  • return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))
  • __repr__ = __str__

主要属性和方法

  • name

    定义spider名字的字符串。唯一

  • allowed_domains

    包含了spider允许爬取的域名(domain)的列表,可选。

  • start_urls

    初始URL元祖/列表。当没有制定特定的URL时,spider将从该列表中开始进行爬取。

  • start_requests(self)

    该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取(默认实现是使用 start_urls 的url)的第一个Request。

    当spider启动爬取并且未指定start_urls时,该方法被调用。

  • parse(self, response)

    当请求url返回网页没有指定回调函数时,默认的Request对象回调函数。用来处理网页返回的response,以及生成Item或者Request对象。

  • log(self, message[, level, component])

    使用 scrapy.log.msg() 方法记录日志信息

CrawlSpider(重点)

CrawlSpider是Spider的派生类,Spider类的设计原则是只爬取start_urls列表中的网页,而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

CrawlSpider类源码参考

  • class CrawlSpider(Spider):
  • rules = ()
  • def __init__(self, *a, **kw):
  • super(CrawlSpider, self).__init__(*a, **kw)
  • self._compile_rules()
  • #首先调用parse()来处理start_urls中返回的response对象
  • #parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url()
  • #设置了跟进标志位True
  • #parse将返回item和跟进了的Request对象
  • def parse(self, response):
  • return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)
  • #处理start_url中返回的response,需要重写
  • def parse_start_url(self, response):
  • return []
  • def process_results(self, response, results):
  • return results
  • #从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
  • def _requests_to_follow(self, response):
  • if not isinstance(response, HtmlResponse):
  • return
  • seen = set()
  • #抽取之内的所有链接,只要通过任意一个'规则',即表示合法
  • for n, rule in enumerate(self._rules):
  • links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
  • #使用用户指定的process_links处理每个连接
  • if links and rule.process_links:
  • links = rule.process_links(links)
  • #将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
  • for link in links:
  • seen.add(link)
  • #构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
  • r = Request(url=link.url, callback=self._response_downloaded)
  • r.meta.update(rule=n, link_text=link.text)
  • #对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
  • yield rule.process_request(r)
  • #处理通过rule提取出的连接,并返回item以及request
  • def _response_downloaded(self, response):
  • rule = self._rules[response.meta['rule']]
  • return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
  • #解析response对象,会用callback解析处理他,并返回request或Item对象
  • def _parse_response(self, response, callback, cb_kwargs, follow=True):
  • #首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
  • #如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
  • #然后再交给process_results处理。返回cb_res的一个列表
  • if callback:
  • #如果是parse调用的,则会解析成Request对象
  • #如果是rule callback,则会解析成Item
  • cb_res = callback(response, **cb_kwargs) or ()
  • cb_res = self.process_results(response, cb_res)
  • for requests_or_item in iterate_spider_output(cb_res):
  • yield requests_or_item
  • #如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
  • if follow and self._follow_links:
  • #返回每个Request对象
  • for request_or_item in self._requests_to_follow(response):
  • yield request_or_item
  • def _compile_rules(self):
  • def get_method(method):
  • if callable(method):
  • return method
  • elif isinstance(method, basestring):
  • return getattr(self, method, None)
  • self._rules = [copy.copy(r) for r in self.rules]
  • for rule in self._rules:
  • rule.callback = get_method(rule.callback)
  • rule.process_links = get_method(rule.process_links)
  • rule.process_request = get_method(rule.process_request)
  • def set_crawler(self, crawler):
  • super(CrawlSpider, self).set_crawler(crawler)
  • self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)

LinkExtractors

  • 使用LinkExtractors 的目的: 提取链接。每个LinkExtractor有唯一的公共方法是 extract_links(),它接收
  • 一个 Response 对象,并返回一个 scrapy.link.Link 对象。
  • scrapy.linkextractors.LinkExtractor(
  • allow = (),
  • deny = (),
  • allow_domains = (),
  • deny_domains = (),
  • deny_extensions = None,
  • restrict_xpaths = (),
  • tags = ('a','area'),
  • attrs = ('href'),
  • canonicalize = True,
  • unique = True,
  • process_value = None
  • )
  • 主要参数:
  • allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
  • deny:与这个正则表达式(或正则表达式列表)匹配的URL一定不提取。
  • allow_domains:会被提取的链接的domains。
  • deny_domains:一定不会被提取链接的domains。
  • restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

rules

  • 在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。如果多个rule
  • 匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。
  • scrapy.spiders.Rule(
  • link_extractor,
  • callback = None,
  • cb_kwargs = None,
  • follow = None,
  • process_links = None,
  • process_request = None
  • )
  • link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。
  • callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接
  • 受一个response作为其第一个参数。
  • 注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来
  • 实现其逻辑,如果覆盖了parse方法,crawlspider将会运行失败。
  • follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如
  • 果callback为None,follow默认设置为True ,否则默认为False
  • process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时
  • 将会调用该函数。该方法主要用来过滤。
  • process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会
  • 调用该函数。 (用来过滤request)

实例:

  • import time
  • import scrapy
  • from ..items import XinlangItem
  • # 导入CrawlSpider: 可以连续爬取网页(翻页爬比较方便)
  • from scrapy.spiders import CrawlSpider, Rule
  • from scrapy.linkextractors import LinkExtractor
  • # 使用CrawlSpider
  • class XinlangnewsSpider(CrawlSpider): #需要继承这个类
  • name = 'xinlangnews'
  • allowed_domains = ['sina.com.cn']
  • start_urls = ['http://roll.news.sina.com.cn/news/gnxw/gdxw1/index_1.shtml']
  • # 定义rules: 提取链接规则
  • rules = [
  • Rule(
  • # 链接提取规则
  • LinkExtractor(
  • allow=('index_\d+\.shtml',), # 要匹配哪些href的链接内容, 支持正则
  • restrict_xpaths=('//div[@class="pagebox"]',), # 限制提取链接的范围, 支持xpath
  • # deny=('index_2.shtml',) # 不爬第2页
  • ),
  • callback='parse_item', # 提取的链接会自动请求, 会自动回调parse_item方法
  • #follow=True, # 是否跟随:是否从提起的新链接中继续提取新链接
  • )
  • ]
  • def parse_item(self, response):
  • # 使用xpath
  • time.sleep(1)
  • news_list = response.xpath('//ul[@class="list_009"]/li')
  • for news in news_list:
  • # 新闻标题
  • news_title = news.xpath('./a/text()').get()
  • # 新闻时间
  • news_time = news.xpath('./span/text()').get()
  • print(news_title,news_time)
  • # item
  • item = XinlangItem()
  • item['newstitle'] = news_title
  • item['newstime'] = news_time
  • yield item
  • #settings.py中
  • BOT_NAME = 'xinlang'
  • SPIDER_MODULES = ['xinlang.spiders']
  • NEWSPIDER_MODULE = 'xinlang.spiders'
  • # Configure item pipelines
  • # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
  • ITEM_PIPELINES = {
  • 'xinlang.pipelines.XinlangPipeline': 300,
  • }
  • ROBOTSTXT_OBEY = False
  • #items.py中
  • import scrapy
  • class XinlangItem(scrapy.Item):
  • newstitle = scrapy.Field()
  • newstime = scrapy.Field()
  • #pipelines.py中
  • from itemadapter import ItemAdapter
  • import pymysql
  • class XinlangPipeline:
  • # 爬虫开始
  • def open_spider(self, spider):
  • print('开始')
  • # 连接MySQL
  • self.db = pymysql.connect(
  • host='IP地址', port=3306,
  • user='root', password='密码',
  • database='spider88', charset='utf8'
  • )
  • self.cursor = self.db.cursor()
  • # 爬虫结束
  • def close_spider(self, spider):
  • print('结束')
  • self.cursor.close()
  • self.db.close()
  • def process_item(self, item, spider):
  • print(spider.name) #
  • #print(f'---- {item} ----')
  • news_title = item['newstitle']
  • news_time = item['newstime']
  • # sql语句
  • sql = 'insert into xinlangnews(newstitle, newtime) values("%s","%s")' \
  • % (news_title, news_time)
  • # 执行和提交sql
  • self.cursor.execute(sql)
  • self.db.commit()
  • return item
CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐