CrawlSpiders

通过下面的命令可以快速创建 CrawlSpider模板 的代码:

.scrapy startproject tencentspider
.scrapy genspider -t crawl tencent tencent.com

上一个案例中,我们通过正则表达式,制作了新的url作为Request请求参数,现在我们可以换个花样...

class scrapy.spiders.CrawlSpider

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

1.爬取腾讯

tencent.py
#!/usr/bin/env python
# -*- coding:utf- -*- import scrapy
# 导入CrawlSpider类和Rule
from scrapy.spiders import CrawlSpider, Rule
# 导入链接规则匹配类,用来提取符合规则的连接
from scrapy.linkextractors import LinkExtractor
from TencentSpider.items import TencentItem class TencentSpider(CrawlSpider):
name = "tencent"
allow_domains = ["hr.tencent.com"]
start_urls = ["http://hr.tencent.com/position.php?&start=0#a"] # Response里链接的提取规则,返回的符合匹配规则的链接匹配对象的列表
pagelink = LinkExtractor(allow=("start=\d+")) rules = [
# 获取这个列表里的链接,依次发送请求,并且继续跟进,调用指定回调函数处理
Rule(pagelink, callback = "parseTencent", follow = True)
] # 指定的回调函数
def parseTencent(self, response):
#evenlist = response.xpath("//tr[@class='even'] | //tr[@class='odd']")
#oddlist = response.xpath("//tr[@class='even'] | //tr[@class='odd']")
#fulllist = evenlist + oddlist
#for each in fulllist:
for each in response.xpath("//tr[@class='even'] | //tr[@class='odd']"):
item = TencentItem()
# 职位名称
item['positionname'] = each.xpath("./td[1]/a/text()").extract()[]
# 详情连接
item['positionlink'] = each.xpath("./td[1]/a/@href").extract()[]
# 职位类别
item['positionType'] = each.xpath("./td[2]/text()").extract()[]
# 招聘人数
item['peopleNum'] = each.xpath("./td[3]/text()").extract()[]
# 工作地点
item['workLocation'] = each.xpath("./td[4]/text()").extract()[]
# 发布时间
item['publishTime'] = each.xpath("./td[5]/text()").extract()[] yield item

CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:

LinkExtractors

class scrapy.linkextractors.LinkExtractor

Link Extractors 的目的很简单: 提取链接。

每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。

Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。

class 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匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。

class 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方法,crawl spider将会运行失败。

  • follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow 默认设置为True ,否则默认为False。

  • process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

  • process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

爬取规则(Crawling rules)

继续用腾讯招聘为例,给出配合rule使用CrawlSpider的例子:

  1. 首先运行

     scrapy shell "http://hr.tencent.com/position.php?&start=0#a"
  2. 导入LinkExtractor,创建LinkExtractor实例对象。:

     from scrapy.linkextractors import LinkExtractor
    
     page_lx = LinkExtractor(allow=('position.php?&start=\d+'))

    allow : LinkExtractor对象最重要的参数之一,这是一个正则表达式,必须要匹配这个正则表达式(或正则表达式列表)的URL才会被提取,如果没有给出(或为空), 它会匹配所有的链接。

    deny : 用法同allow,只不过与这个正则表达式匹配的URL不会被提取)。它的优先级高于 allow 的参数,如果没有给出(或None), 将不排除任何链接。

  3. 调用LinkExtractor实例的extract_links()方法查询匹配结果:

     page_lx.extract_links(response)
  4. 没有查到:

     []
  5. 注意转义字符的问题,继续重新匹配:

     page_lx = LinkExtractor(allow=('position\.php\?&start=\d+'))
    # page_lx = LinkExtractor(allow = ('start=\d+')) page_lx.extract_links(response)

CrawlSpider 版本

那么,scrapy shell测试完成之后,修改以下代码

#提取匹配 'http://hr.tencent.com/position.php?&start=\d+'的链接
page_lx = LinkExtractor(allow = ('start=\d+')) rules = [
#提取匹配,并使用spider的parse方法进行分析;并跟进链接(没有callback意味着follow默认为True)
Rule(page_lx, callback = 'parse', follow = True)
]

这么写对吗?

不对!千万记住 callback 千万不能写 parse,再次强调:由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。

运行:

 scrapy crawl tencent

Logging

Scrapy提供了log功能,可以通过 logging 模块使用。

可以修改配置文件settings.py,任意位置添加下面两行,效果会清爽很多。

LOG_FILE = "TencentSpider.log"
LOG_LEVEL = "INFO"

Log levels

  • Scrapy提供5层logging级别:

  • CRITICAL - 严重错误(critical)

  • ERROR - 一般错误(regular errors)
  • WARNING - 警告信息(warning messages)
  • INFO - 一般信息(informational messages)
  • DEBUG - 调试信息(debugging messages)

logging设置

通过在setting.py中进行以下设置可以被用来配置logging:

  1. LOG_ENABLED 默认: True,启用logging
  2. LOG_ENCODING 默认: 'utf-8',logging使用的编码
  3. LOG_FILE 默认: None,在当前目录里创建logging输出文件的文件名
  4. LOG_LEVEL 默认: 'DEBUG',log的最低级别
  5. LOG_STDOUT 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。例如,执行 print "hello" ,其将会在Scrapy log中显示。

items.py

# -*- coding: utf- -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html import scrapy class TencentItem(scrapy.Item):
# define the fields for your item here like:
# 职位名
positionname = scrapy.Field()
# 详情连接
positionlink = scrapy.Field()
# 职位类别
positionType = scrapy.Field()
# 招聘人数
peopleNum = scrapy.Field()
# 工作地点
workLocation = scrapy.Field()
# 发布时间
publishTime = scrapy.Field()

pipelines.py

# -*- coding: utf- -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html import json class TencentPipeline(object):
def __init__(self):
self.filename = open("tencent.json", "w") def process_item(self, item, spider):
text = json.dumps(dict(item), ensure_ascii = False) + ",\n"
self.filename.write(text.encode("utf-8"))
return item def close_spider(self, spider):
self.filename.close()

2.爬取东莞

dongdong.py

# -*- coding: utf- -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from newdongguan.items import NewdongguanItem class DongdongSpider(CrawlSpider):
name = 'dongdong'
allowed_domains = ['wz.sun0769.com']
start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page='] # 每一页的匹配规则
pagelink = LinkExtractor(allow=("type=4"))
# 每一页里的每个帖子的匹配规则
contentlink = LinkExtractor(allow=(r"/html/question/\d+/\d+.shtml")) rules = (
# 本案例的url被web服务器篡改,需要调用process_links来处理提取出来的url
Rule(pagelink, process_links = "deal_links"),
Rule(contentlink, callback = "parse_item")
) # links 是当前response里提取出来的链接列表
def deal_links(self, links):
for each in links:
each.url = each.url.replace("?","&").replace("Type&","Type?")
return links def parse_item(self, response):
item = NewdongguanItem()
# 标题
item['title'] = response.xpath('//div[contains(@class, "pagecenter p3")]//strong/text()').extract()[]
# 编号
item['number'] = item['title'].split(' ')[-].split(":")[-]
# 内容,先使用有图片情况下的匹配规则,如果有内容,返回所有内容的列表集合
content = response.xpath('//div[@class="contentext"]/text()').extract()
# 如果没有内容,则返回空列表,则使用无图片情况下的匹配规则
if len(content) == :
content = response.xpath('//div[@class="c1 text14_2"]/text()').extract()
#"".join(content).strip()把列表转换成字符串,去掉空格
item['content'] = "".join(content).strip()
else:
item['content'] = "".join(content).strip()
# 链接
item['url'] = response.url yield item

items.py

# -*- coding: utf- -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html import scrapy class NewdongguanItem(scrapy.Item):
# define the fields for your item here like:
# 标题
title = scrapy.Field()
# 编号
number = scrapy.Field()
# 内容
content = scrapy.Field()
# 链接
url = scrapy.Field()

3.pipelines.py

# -*- coding: utf- -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html import codecs
import json class NewdongguanPipeline(object):
def __init__(self):
# 创建一个文件
self.filename = codecs.open("donggguan.json", "w", encoding = "utf-8") def process_item(self, item, spider):
# 中文默认使用ascii码来存储,禁用后默认为Unicode字符串
content = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.filename.write(content)
return item def close_spider(self, spider):
self.filename.close()

最新文章

  1. [Unity3D]自己动手重制坦克舰队ArmadaTank
  2. 用最简单的方式在C#中使用多线程加速耗时的图像处理算法的执行(多核机器)。
  3. 【转】C#调用DLL
  4. MFC对话框中使用CHtmlEditCtrl
  5. 小数5.2500四舍五入保留1位小数的java算法之一
  6. 不再折腾-----hadoop-1.2.1完全分布式安装
  7. 支持向量机(SVM)算法的matlab的实现
  8. Android ExpandableListActivity的简单介绍及小例子
  9. JS 事件冒泡整理 浏览器的事件流
  10. C语言高速入口系列(七)
  11. sass学习--安装ruby
  12. Windows下RabbitMQ安装及入门
  13. python访问mysql和redis
  14. -1-1 java 基础语法 java关键字 java 注释 常量 语句 运算符 函数 数组定义
  15. 阿里云 windows 2008 使用Thinkphp5 captcha验证码不显示问题
  16. 【Python】This inspection detects names that should resolve but don't. Due to dynamic dispatch and duck
  17. CentOS 7 下使用yum安装MySQL5.7.20 最简单图文详解
  18. bzoj3991 lca+dfs序应用+set综合应用
  19. 11.webview、shareSDK
  20. KVM虚拟化研究-1

热门文章

  1. SCM_SVN_CVS
  2. ssrf绕过总结
  3. Valid Mountain Array LT941
  4. SQL Server 查询中文字段返回为空
  5. css字符串转换为类map对象及反转
  6. 2017/2/10:Manven简介与项目管理(入门)
  7. 从hash算法到java hashcode()
  8. canvas 实现赛车游戏
  9. Eclipse使用Git管理项目
  10. 2019.01.14 bzoj4530: [Bjoi2014]大融合(线段树合并)