scrapy 英文文档:https://docs.scrapy.org/en/latest/index.html
scrapy 中文文档:https://www.osgeo.cn/scrapy/index.html
参考:https://piaosanlang.gitbooks.io/spiders/content/
github 爬虫 框架
- :https://github.com/search?q=爬虫+框架
- :https://github.com/search?q=spider
爬虫框架中比较好用的是 Scrapy 和 PySpider。
文档:https://docs.pyspider.org/en/latest/Quickstart/
github:https://github.com/binux/pyspider
优点:分布式框架,上手更简单,操作更加简便,因为它增加了 WEB 界面,写爬虫迅速,集成了phantomjs,可以用来抓取js渲染的页面。
缺点:自定义程度低
文档:https://docs.scrapy.org/en/latest/
中文文档 ( 版本比较旧 ) :https://www.osgeo.cn/scrapy/intro/overview.html
优点:自定义程度高,比 PySpider 更底层一些,适合学习研究,需要学习的相关知识多,拿来研究分布式和多线程等等是最合适不过的。
缺点:非分布式框架(可以用 scrapy-redis 分布式框架)
通常,运行scrapy爬虫的方式是在命令行输入scrapy crawl
,调试的常用方式是在命令行输入scrapy shell
。总的来说,调试方法比较单一。其实,还有两种调试方法,可以在pycharm中实现调试。
首先,在项目文件 scrapy.cfg 的同级建立 main.py 文件(注意,必须是同级建立),在其中键入如下代码:
from scrapy import cmdline
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# 你需要将此处的spider_name替换为你自己的爬虫名称
cmdline.execute(['scrapy', 'crawl', 'spider_name'])
# 或者 cmdline.execute('scrapy crawl spider_name'.split(' '))
pass
在其余爬虫文件中设置断点后,运行main.py,即可实现在pycharm中的调试。
在项目文件scrapy.cfg的同级建立main.py文件(注意,必须是同级建立),在其中键入如下代码:
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
if __name__ == '__main__':
process = CrawlerProcess(get_project_settings())
# 你需要将此处的spider_name替换为你自己的爬虫名称
process.crawl('spider_name')
process.start()
在其余爬虫文件中设置断点后,运行main.py,即可实现在pycharm中的调试。
两种方式都很简单实用,值得掌握。
selenium 在爬虫中的应用
- 获取动态网页中的数据,一些动态的数据我们在获取的源码中并没有显示的之一类动态加载数据
- 可用于模拟登录
安装:pip install selenium
驱动下载:http://chromedriver.storage.googleapis.com/index.html
新一代爬虫利器 -- Playwright:https://zhuanlan.zhihu.com/p/499597451
github:https://github.com/microsoft/playwright
Playwright for TypeScript, JavaScript, Python, .NET, or Java
Playwright (剧作家) 是专门为满足端到端测试的需求而创建的。Playwright支持所有现代渲染引擎,包括Chromium,WebKit和Firefox。在Windows,Linux和macOS上进行测试,本地或CI,无头或以Google Chrome for Android和Mobile Safari的本机移动仿真为标题。
它可以通过单个API自动执行 Chromium,Firefox 和 WebKit 浏览器,连代码都不用写,就能实现自动化功能。虽然测试工具 selenium 具有完备的文档,但是其学习成本让一众小白们望而却步,对比之下 playwright-python 简直是小白们的神器。
提示:playwright 还可支持移动端的浏览器模拟。
github:https://github.com/ClericPy/ichrome
基于 Chrome Devtools Protocol(CDP) 和 python3.7+ 来人为的控制 Chrome
pychrome 也是 Google Chrome Dev Protocol [threading base] 的一个 Python 包
scrapy-cookbook ( 中文版 ):https://scrapy-cookbook.readthedocs.io/zh_CN/latest/index.html
Scrapy 官网文档 ( 英文版 ):https://docs.scrapy.org/en/latest/index.html
Scrapy 是一个快速的高级网络抓取和网络抓取框架,用于抓取网站并从其页面中提取结构化数据。它可以用于广泛的目的,从数据挖掘到监控和自动化测试。
Scrapy at a glance
Installation guide
Scrapy Tutorial
Examples
基本概念
- Command line tool
- Spiders
- Selectors
- Items
- Item Loaders
- Scrapy shell
- Item Pipeline
- Feed exports
- Requests and Responses
- Link Extractors
- Settings
- Exceptions
内置服务
- Logging
- Stats Collection
- Sending e-mail
- Telnet Console
一些常见问题
- Frequently Asked Questions
- Debugging Spiders
- Spiders Contracts
- Common Practices
- Broad Crawls
- Using your browser’s Developer Tools for scraping
- Selecting dynamically-loaded content
- Debugging memory leaks
- Downloading and processing files and images
- Deploying Spiders
- AutoThrottle extension
- Benchmarking
- Jobs: pausing and resuming crawls
- Coroutines
- asyncio
Scrapy 扩展
- Architecture overview
- Downloader Middleware
- Spider Middleware
- Extensions
- Signals
- Scheduler
- Item Exporters
- Components
- Core API
Scrapy 官方文档:https://docs.scrapy.org/en/latest/
下图显示了 Scrapy 体系结构及其概述 系统内发生的数据流的组件和概述 (由红色箭头显示)。包括组件的简要说明 下面包含有关它们的更多详细信息的链接。
处理流程图:
Scrapy 中的数据流由执行引擎控制,流程如下:
- 引擎 首先从自己编写的 spider 中读取起始 url,然后封装成 Request对象
- 引擎 把 "封装后的Request对象" 传递给 调度器 ( 调度器主要作用就是管理、调度url,可以简单的看作是一个 "间接的队列",对 Requestd对象 管理、过滤 等操作)。
- 引擎 请求 调度器,调度器返回一个 Request对象 给引擎。
- 引擎 将 Request对象 发送到下载器。下载器会将请求通过下载器中间件。( process_request() )
- 下载器完成页面下载后,下载器会将生成的 响应Response 通过下载器中间件(process_response()),最后将其发送到引擎。
- 引擎接收来自下载器的 响应 并将其发送给 自己编写的 spider 进行处理,但是在发送之前会先 传递 通过spider中间件(参见process_spider_input())。
- 自己编写的 spider 处理响应并返回 "抓取的数据Item" 及(跟进的)新的Request给引擎,通过蜘蛛中间件(参见process_spider_output())。
- 引擎将 "抓取的数据Item" 发送到 pipeline,将 "新的请求" 发送到 调度程序。并继续从调度器中获取 下一个 "Request对象" 来抓取。
- 该过程重复(从步骤 3 开始),直到没有更多地 request对象 ,最后关闭引擎。
整个工作流程
- 1.引擎 将爬虫中起始的url构造成request对象,并传递给调度器。
- 2.引擎 从 调度器 中获取到request对象然后交给下载器。
- 3.由 下载器 来获取到页面源代码,并封装成response对象,并返回给引擎。
- 4.引擎 将获取到的response对象传递给 spider,由 spider 对数据进行解析(parse),并返回给引擎
- 5.引擎将数据传递给 pipeline 进行数据持久化保存或进一步的数据处理
- 6.在此期间如果spider中提取到的并不是数据。而是子页面ur.可以进一步提交给调度器,进而重复 步骤2 的过程
五个功能模块
三大中间件
Scrapy 是用 Python 开发的一个快速、高层次的 web 抓取框架;Scrapy 用途广泛,可以用于数据挖掘、监测和自动化测试。Scrapy 使用了 Twisted 异步网络库来处理网络通讯。
windows 安装:pip install scrapy
Linux 安装:sudo pip install Scrapy
验证安装:输入 Scrapy 或者 scrapy(大小写都可以)。如果提示如下命令,就证明安装成功。
注意:linux依然严格区分大小写
大致流程:
在运行 crawl 时添加 -a 可以传递 Spider 参数,spider 参数一般用来定义初始URL或者指定限制爬取网站的部分。 您也可以使用其来配置spider的任何功能。
scrapy crawl myspider -a category=electronics
Spider 在构造器 (constructor) 中获取参数:
import scrapy
class MySpider(Spider):
name = 'myspider'
def __init__(self, category=None, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.start_urls = ['http://www.example.com/categories/%s' % category]
脚本运行 Scrapy:https://docs.scrapy.org/en/latest/topics/practices.html
在开始爬取之前必须创建一个新的 Scrapy 项目。 进入打算存储代码的目录中。运行下列命令: scrapy startproject tutorial
该命令将会创建包含下列内容的 tutorial 目录,这些文件分别是:
scrapy.cfg: 项目的配置文件;(用于发布到服务器)
tutorial/: 该项目文件夹。之后将在此编写Python代码。
tutorial/items.py: 项目中的item文件;(定义结构化数据字段field).
tutorial/pipelines.py: 项目中的pipelines文件;(用于存放执行后期数据处理的功能,定义如何存储结构化数据)
tutorial/settings.py: 项目的设置文件;(如何修改User-Agent,设置爬取时间间隔,设置代理,配置中间件等等)
tutorial/spiders/: 放置spider代码的目录;(编写爬取网站规则)
windows 下创建:
Pycharm 打开 Scrapy 工程:
Item 定义结构化数据字段,用来保存爬取到的数据;其使用方法和python字典类似。可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field 的类属性来定义一个 Item。
示例:腾讯招聘中获取 职位名称、职位详情页url、职位类别、人数、工作地点以及发布时间。 对此,在item中定义相应的字段。编辑 tutorial 目录中的 items.py 文件:
import scrapy
class RecruitItem(scrapy.Item):
name = scrapy.Field()
detailLink = scrapy.Field()
catalog = scrapy.Field()
recruitNumber = scrapy.Field()
workLocation = scrapy.Field()
publishTime = scrapy.Field()
创建一个 Spider,必须继承 'scrapy.Spider' 类, 需要定义以下三个属性:
示例 Spider 代码,保存在 tutorial/spiders 目录下的 tencent_spider.py 文件中:
import scrapy
class RecruitSpider(scrapy.spiders.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
f = open('tengxun.txt', 'wb')
f.write(response.body)
f.close()
进入项目的根目录,执行下列命令启动 spider:
scrapy crawl tencent
crawl tencent 启动用于爬取 tencent 的 spider,您将得到类似的输出:
现在,查看当前目录,会注意到有文件被创建了: tengxun.txt。
刚才发生了什么?
Scrapy 内置的 Selectors 模块提供了对 XPath 和 CSS Selector 的支持。也可以单独拿出来使用
单独使用 示例:
from scrapy import Selector
temp_string = '''
Harry Potter
29.99
Learning XML
39.95
'''
if __name__ == '__main__':
s = Selector(text=temp_string)
print(s.xpath('//book[1]/title/text()').extract_first())
print(s.xpath('//book[1]/price/text()').extract_first())
pass
XPath 表达式的例子及对应的含义:
/html/head/title 选择文档中 标签内的 元素
/html/head/title/text() 选择上面提到的 元素的文字
//td 选择所有的 元素
//div[@class="mine"] 选择所有具有 class="mine" 属性的 div 元素
Selector 有四个基本的方法:
xpath() 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表 。
css() 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表.
extract() 序列化该节点为unicode字符串并返回list。
re() 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。
scrapy shell
尝试 Selector 选择器
为了介绍 Selector的使用方法,接下来我们将要使用内置的 scrapy shell 。Scrapy Shell 需要您预装好 IPython (一个扩展的Python终端)。您需要进入项目的根目录,执行下列命令来启动 shell:
scrapy shell "http://hr.tencent.com/position.php?&start=0#a"
注解: 当您在终端运行 Scrapy 时,请一定记得给 url 地址加上引号,否则包含参数的 url (例如 & 字符)会导致 Scrapy 运行失败。
shell 的输出类似:
当 shell 载入后,将得到一个包含 response 数据的本地 response 变量。
输入 response.body 将输出 response 的包体, 输出 response.headers 可以看到 response 的包头。
当输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象。
此时,可以通过使用 response.selector.xpath() 或 response.selector.css() 来对 response 进行查询。
scrapy 对 response.selector.xpath() 及 response.selector.css() 提供了一些快捷方式,例如 response.xpath() 或 response.css()
response.xpath('//title')
[\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058]
response.xpath('//title').extract()
[u'\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058 ']
print response.xpath('//title').extract()[0]
职位搜索 | 社会招聘 | Tencent 腾讯招聘
response.xpath('//title/text()')
response.xpath('//title/text()')[0].extract()
u'\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058'
print response.xpath('//title/text()')[0].extract()
职位搜索 | 社会招聘 | Tencent 腾讯招聘
response.xpath('//title/text()').re('(\w+):')
[u'\u804c\u4f4d\u641c\u7d22',
u'\u793e\u4f1a\u62db\u8058',
u'Tencent',
u'\u817e\u8baf\u62db\u8058']
提取数据
现在,从页面中提取些有用的数据。
# 通过 XPath 选择该页面中网站列表里所有 lass=even 元素
site = response.xpath('//*[@class="even"]')
# 职位名称:
print site[0].xpath('./td[1]/a/text()').extract()[0]
# TEG15-运营开发工程师(深圳)
# 职位名称详情页:
print site[0].xpath('./td[1]/a/@href').extract()[0]
position_detail.php?id=20744&keywords=&tid=0&lid=0
# 职位类别:
print site[0].xpath('./td[2]/text()').extract()[0]
# 技术类
对于 .xpath() 调用返回 selector 组成的 list, 因此可以拼接更多的 .xpath() 来进一步获取某个节点。
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
print name, detailLink, catalog,recruitNumber,workLocation,publishTime
在我们的 tencent_spider.py 文件修改成如下代码:
import scrapy
class RecruitSpider(scrapy.spiders.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
print name, detailLink, catalog,recruitNumber,workLocation,publishTime
如图所示:
现在尝试再次爬取 hr.tencent.com,您将看到爬取到的网站信息被成功输出:
scrapy crawl tencent
运行过程:
编写 item
Item 对象是自定义的 python 字典。可以使用标准的字典语法来获取到其每个字段的值。输入 `scrapy shell'
import scrapy
class RecruitItem(scrapy.Item):
name = scrapy.Field()
detailLink = scrapy.Field()
catalog = scrapy.Field()
recruitNumber = scrapy.Field()
workLocation = scrapy.Field()
publishTime = scrapy.Field()
item = RecruitItem()
item['name'] = 'sanlang'
item['name']
'sanlang'
一般来说,Spider 将会将爬取到的数据以 Item 对象返回。所以为了将爬取的数据返回,最终 tencent_spider.py 代码将是:
import scrapy
from tutorial.items import RecruitItem
class RecruitSpider(scrapy.spiders.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name.encode('utf-8')
item['detailLink']=detailLink.encode('utf-8')
item['catalog']=catalog.encode('utf-8')
item['recruitNumber']=recruitNumber.encode('utf-8')
item['workLocation']=workLocation.encode('utf-8')
item['publishTime']=publishTime.encode('utf-8')
yield item
现在对 hr.tencent.com 进行爬取将会产生 RecruitItem 对象:
命令行 保存 抓取的数据
最简单存储爬取的数据的方式是使用 Feed exports:
scrapy crawl tencent -o items.json
该命令将采用 JSON 格式对爬取的数据进行序列化,生成 items.json 文件。
如果需要对爬取到的item做更多更为复杂的操作,您可以编写 Item Pipeline 。 类似于我们在创建项目时对Item做的,用于您编写自己的 tutorial/pipelines.py 也被创建。 不过如果您仅仅想要保存item,您不需要实现任何的pipeline。
编写 Pipelines
item pipiline 组件是一个独立的 Python 类,必须实现 process_item 方法:
- process_item(self, item, spider):当 Item 在 Spider 中被收集之后,都需要调用该方法。参数: item - 爬取的结构化数据。 spider – 爬取该 item 的 spider
- open_spider(self, spider):当 spider 被开启时,这个方法被调用。参数:spider – 被开启的spider
- close_spider(spider):当 spider 被关闭时,这个方法被调用。参数:spider – 被关闭的spider
将 item 写入 JSON 文件
以下 pipeline 将所有爬取到的 item,存储到一个独立地 items.json 文件,每行包含一个序列化为 'JSON' 格式的 'item':
import json
class JsonWriterPipeline(object):
def __init__(self):
self.file = open('items.json', 'wb')
def process_item(self, item, spider):
line = json.dumps(dict(item),ensure_ascii=False) + "\n"
self.file.write(line)
return item
启用一个Item Pipeline组件
为了启用 Item Pipeline 组件,必须将它的类添加到 settings.py 文件 ITEM_PIPELINES 配置,就像下面这个例子:
ITEM_PIPELINES = {
#'tutorial.pipelines.PricePipeline': 300,
'tutorial.pipelines.JsonWriterPipeline': 800,
}
分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过 pipeline,通常将这些数字定义在0-1000范围内。
将 item 写入 MongoDB
- from_crawler(cls, crawler):如果使用,这类方法被调用创建爬虫管道实例。必须返回管道的一个新实例。crawler提供存取所有Scrapy核心组件配置和信号管理器; 对于pipelines这是一种访问配置和信号管理器 的方式。参数: crawler (Crawler object) – crawler that uses this pipeline
例子中,我们将使用 pymongo 将 Item 写到 MongoDB。MongoDB 的地址和数据库名称在 Scrapy setttings.py 配置文件中;这个例子主要是说明如何使用 from_crawler() 方法
import pymongo
class MongoPipeline(object):
collection_name = 'scrapy_items'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert(dict(item))
return item
4、Spider、CrawlSpider
scrapy 为我们提供了5种 spider 用于构造请求,解析数据、返回 item。
常用的就是:scrapy.spider、scrapy.crawlspider两种。
class scrapy.spider.Spider
Spider类 定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接) 以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider 就是定义爬取的动作及分析某个网页(或者是有些网页)的地方。
Spider 是最简单的 spider。每个 spider 必须继承自该类。Spider 并没有提供什么特殊的功能。其仅仅请求给定的 start_urls / start_requests,并根据返回的结果调用 spider 的 parse 方法。
- name:定义 spider 名字的字符串。例如,如果spider爬取 mywebsite.com ,该 spider 通常会被命名为 mywebsite
- allowed_domains:可选。包含了spider允许爬取的域名(domain)列表(list)
- start_urls:初始 URL 列表。当没有制定特定的 URL 时,spider 将从该列表中开始进行爬取。
- start_requests():当 spider 启动爬取并且未指定 start_urls 时,该方法被调用。如果您想要修改最初爬取某个网站。
- parse(self, response):当请求 url 返回网页没有指定回调函数时,默认下载回调方法。参数:response (Response) – 返回网页信息的 response
- log(message[, level, component]):使用 scrapy.log.msg() 方法记录(log)message。 更多数据请参见 Logging
下面是 spider 常用到的 属性 和 方法。( 想要了解更多,可以查看源码 )
属性、方法
功能
简述
name
爬虫的名称
启动爬虫的时候会用到
start_urls
起始 url
是一个列表,默认被 start_requests 调用
allowd_doamins
对 url 进行的简单过滤
当请求 url 没有被 allowd_doamins 匹配到时,会报一个非常恶心的错,
start_requests()
第一次请求
自己的 spider 可以重写,突破一些简易的反爬机制
custom_settings
定制 settings
可以对每个爬虫定制 settings 配置
from_crawler
实例化入口
在 scrapy 的各个组件的源码中,首先执行的就是它
关于 spider 我们可以定制 start_requests、可以单独的设置 custom_settings、也可以设置请
例如,如果您需要在启动时以 POST 登录某个网站,你可以这么写:
class MySpider(scrapy.Spider):
name = 'myspider'
def start_requests(self):
return [scrapy.FormRequest("http://www.example.com/login",
formdata={'user': 'john', 'pass': 'secret'},
callback=self.logged_in)]
def logged_in(self, response):
# here you would extract links to follow and return Requests for
# each of them, with another callback
pass
Spider 示例
让我们来看一个例子:
import scrapy
class MySpider(scrapy.Spider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = [
'http://www.example.com/1.html',
'http://www.example.com/2.html',
'http://www.example.com/3.html',
]
def parse(self, response):
self.log('A response from %s just arrived!' % response.url)
另一个在单个回调函数中返回多个 Request 以及 Item 的例子:
import scrapy
from myproject.items import MyItem
class MySpider(scrapy.Spider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = [
'http://www.example.com/1.html',
'http://www.example.com/2.html',
'http://www.example.com/3.html',
]
def parse(self, response):
sel = scrapy.Selector(response)
for h3 in response.xpath('//h3').extract():
yield MyItem(title=h3)
for url in response.xpath('//a/@href').extract():
yield scrapy.Request(url, callback=self.parse)
案例:腾讯招聘网翻页功能
import scrapy
from tutorial.items import RecruitItem
import re
class RecruitSpider(scrapy.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog =None
if sel.xpath('./td[2]/text()'):
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
#print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name.encode('utf-8')
item['detailLink']=detailLink.encode('utf-8')
if catalog:
item['catalog']=catalog.encode('utf-8')
item['recruitNumber']=recruitNumber.encode('utf-8')
item['workLocation']=workLocation.encode('utf-8')
item['publishTime']=publishTime.encode('utf-8')
yield item
nextFlag = response.xpath('//*[@id="next"]/@href')[0].extract()
if 'start' in nextFlag:
curpage = re.search('(\d+)',response.url).group(1)
page =int(curpage)+10
url = re.sub('\d+',str(page),response.url)
print url
yield scrapy.Request(url, callback=self.parse)
执行:scrapy crawl tencent -L INFO
scrapy.spiders.CrawlSpider
CrawlSpider 定义了一些规则(rule)来提供跟进 link 的方便的机制。除了从 Spider 继承过来的(您必须提供的)属性外(name、allow_domains),其提供了一个新的属性:
- rules:包含一个(或多个) 规则对象的集合(list)。 每个Rule对爬取网站的动作定义了特定操作。 如果多个 rule 匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。
- parse_start_url(response):当 start_url 的请求返回时,该方法被调用
爬取规则(Crawling rules)
class scrapy.contrib.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
- link_extractor:其定义了如何从爬取到的页面中提取链接。
- callback:指定 spider 中哪个函数将会被调用。 从 link_extractor 中每获取到链接时将会调用该函数。该回调函数接受一个response 作为其第一个参数。注意:当编写爬虫规则时,请避免使用 parse作为回调函数。由于 CrawlSpider 使用 parse 方法来实现其逻辑,如果您覆盖了 parse方法,crawl spider将会运行失败。
- cb_kwargs:包含传递给回调函数的参数 (keyword argument) 的字典。
- follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow默认设置为True ,否则默认为False。
- process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法常用于过滤参数
- process_request:指定该spider中哪个的函数将会被调用,该规则提取到每个request时都会调用该函数 (用来过滤request)
CrawlSpider 案例
还是以腾讯招聘为例,给出配合 rule 使用 CrawlSpider 的例子:
首先运行
scrapy shell "http://hr.tencent.com/position.php?&start=0#a"
导入匹配规则:
from scrapy.linkextractors import LinkExtractor
page_lx = LinkExtractor(allow=('position.php?&start=\d+'))
查询匹配结果:
page_lx.extract_links(response)
没有查到:
page_lx = LinkExtractor(allow=(r'position\.php\?&start=\d+'))
page_lx.extract_links(response)
[Link(url='http://hr.tencent.com/position.php?start=10', text='2', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=20', text='3', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=30', text='4', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=40', text='5', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=50', text='6', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=60', text='7', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=70', text='...', fragment='', nofollow=False),
Link(url='http://hr.tencent.com/position.php?start=1300', text='131', fragment='', nofollow=False)]
len(page_lx.extract_links(response))
那么,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,一定运行有错误!!
保存代码为 tencent_crawl.py
# -*- coding:utf-8 -*-
import scrapy
from tutorial.items import RecruitItem
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class RecruitSpider(CrawlSpider):
name = "tencent_crawl"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
#提取匹配 'http://hr.tencent.com/position.php?&start=\d+'的链接
page_lx = LinkExtractor(allow=('start=\d+'))
rules = [
#提取匹配,并使用spider的parse方法进行分析;并跟进链接(没有callback意味着follow默认为True)
Rule(page_lx, callback='parseContent',follow=True)
]
def parseContent(self, response):
print response.url
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog =None
if sel.xpath('./td[2]/text()'):
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
#print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name.encode('utf-8')
item['detailLink']=detailLink.encode('utf-8')
if catalog:
item['catalog']=catalog.encode('utf-8')
item['recruitNumber']=recruitNumber.encode('utf-8')
item['workLocation']=workLocation.encode('utf-8')
item['publishTime']=publishTime.encode('utf-8')
yield item
可以修改配置文件settings.py,添加 LOG_LEVEL='INFO'
运行: scrapy crawl tencent_crawl
process_links 参数:动态网页爬取,动态 url 的处理
在爬取 https://bitsharestalk.org 的时候,发现网站会为每一个 url 增加一个 sessionid 属性,可能是为了标记用户访问历史,而且这个 seesionid 随着每次访问都会动态变化,这就为爬虫的去重处理(即标记已经爬取过的网站)和提取规则增加了难度。
比如:General Discussion 会变成 https://bitsharestalk.org/index.phpPHPSESSID=9771d42640ab3c89eb77e8bd9e220b53&board=5.0
下面介绍几种处理方法
仅适用你的爬虫使用的是 scrapy.contrib.spiders.CrawlSpider,在这个内置爬虫中,你提取 url 要通过 Rule类来进行提取,其自带了对提取后的 url 进行加工的函数。
rules = (
Rule(
LinkExtractor(
allow=(
r"https://bitsharestalk\.org/index\.php\?PHPSESSID\S*board=\d+\.\d+$",
r"https://bitsharestalk\.org/index\.php\?board=\d+\.\d+$"
)
),
process_links='link_filtering' # 默认函数process_links
),
Rule(
LinkExtractor(
allow=(
r" https://bitsharestalk\.org/index\.php\?PHPSESSID\S*topic=\d+\.\d+$",
r"https://bitsharestalk\.org/index\.php\?topic=\d+\.\d+$",
),
),
callback="extractPost",
follow=True, process_links='link_filtering'
),
Rule(
LinkExtractor(
allow=(
r"https://bitsharestalk\.org/index\.php\?PHPSESSID\S*action=profile;u=\d+$",
r"https://bitsharestalk\.org/index\.php\?action=profile;u=\d+$",
),
),
callback="extractUser",
process_links='link_filtering'
)
)
def link_filtering(self, links):
ret = []
for link in links:
url = link.url
# print "This is the yuanlai ", link.url
urlfirst, urllast = url.split(" ? ")
if urllast:
link.url = urlfirst + " ? " + urllast.split(" & ", 1)[1]
# print link.url
return links
link_filtering() 函数对 url 进行了处理,过滤掉了 sessid,关于 Rule类的 process_links 函数和 links 类,官方文档中并没有给出介绍,给出一个参考 https://groups.google.com/forum/#!topic/scrapy-users/RHGtm_2GO1M(也许需要梯子,你懂得)
如果你是自己实现的爬虫,那么 url 的处理更是可定制的,只需要自己处理一下就可以了。
process_request 参数:修改请求参数
import re
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class WeiboSpider(CrawlSpider):
name = 'weibo'
allowed_domains = ['weibo.com']
# 不加www,则匹配不到 cookie, get_login_cookie()方法正则代完善
start_urls = ['http://www.weibo.com/u/1876296184']
rules = (
Rule(
# 微博个人页面的规则,或/u/或/n/后面跟一串数字
LinkExtractor(allow=r'^http:\/\/(www\.)?weibo.com/[a-z]/.*'),
process_request='process_request',
callback='parse_item', follow=True
),
)
cookies = None
def process_request(self, request):
link = request.url
page = re.search(r'page=\d*', link).group()
tp = re.search(r'type=\d+', link).group()
new_request = request.replace(
cookies=self.cookies,
url='.../questionType?' + page + "&" + tp
)
return new_request
5、Logging
Scrapy 提供了 log 功能。您可以通过 logging 模块使用。
Log levels
Scrapy 提供5层 logging 级别:
- CRITICAL --- 严重错误(critical)
- ERROR --- 一般错误(regular errors)
- WARNING --- 警告信息(warning messages)
- INFO --- 一般信息(informational messages)
- DEBUG --- 调试信息(debugging messages)
默认情况下 python 的 logging 模块将日志打印到了标准输出中,且只显示了大于等于 WARNING 级别的日志,这说明默认的日志级别设置为 WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG,默认的日志格式为DEBUG级别
如何设置 log 级别
您可以通过终端选项(command line option) --loglevel/-L 或 LOG_LEVEL 来设置log级别。
-
scrapy crawl tencent_crawl -L INFO
-
可以修改配置文件 settings.py,添加 LOG_LEVEL='INFO'
scrapy crawl tencent_crawl -L INFO
也可以修改配置文件settings.py,添加 LOG_LEVEL='INFO'
在 Spider 中添加 log
Scrapy 为每个 Spider 实例记录器提供了一个 logger,可以这样访问:
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://scrapinghub.com']
def parse(self, response):
self.logger.info('Parse function called on %s', response.url)
logger 是用 Spider 的名称创建的,但是你可以用你想要的任何自定义 logging。例如:
import logging
import scrapy
logger = logging.getLogger('zhangsan')
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://scrapinghub.com']
def parse(self, response):
logger.info('Parse function called on %s', response.url)
Logging 设置
以下设置可以被用来配置logging:
LOG_ENABLED 默认: True,启用logging
LOG_ENCODING 默认: 'utf-8',logging使用的编码
LOG_FILE 默认: None,logging输出的文件名
LOG_LEVEL 默认: 'DEBUG',log的最低级别
LOG_STDOUT 默认: False。如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。
例如,执行 print 'hello' ,其将会在Scrapy log中显示。
案例 (一) ( self.logger )
tencent_crawl.py 添加日志信息如下:
'''
添加日志信息
'''
print 'print',response.url
self.logger.info('info on %s', response.url)
self.logger.warning('WARNING on %s', response.url)
self.logger.debug('info on %s', response.url)
self.logger.error('info on %s', response.url)
完整版如下:
# -*- coding:utf-8 -*-
import scrapy
from tutorial.items import RecruitItem
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class RecruitSpider(CrawlSpider):
name = "tencent_crawl"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
#提取匹配 'http://hr.tencent.com/position.php?&start=\d+'的链接
page_lx = LinkExtractor(allow=('start=\d+'))
rules = [
#提取匹配,并使用spider的parse方法进行分析;并跟进链接(没有callback意味着follow默认为True)
Rule(page_lx, callback='parseContent',follow=True)
]
def parseContent(self, response):
#print("print settings: %s" % self.settings['LOG_FILE'])
'''
添加日志信息
'''
print 'print',response.url
self.logger.info('info on %s', response.url)
self.logger.warning('WARNING on %s', response.url)
self.logger.debug('info on %s', response.url)
self.logger.error('info on %s', response.url)
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog =None
if sel.xpath('./td[2]/text()'):
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
#print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name
item['detailLink']=detailLink
if catalog:
item['catalog']=catalog
item['recruitNumber']=recruitNumber
item['workLocation']=workLocation
item['publishTime']=publishTime
yield item
在 settings 文件中,修改添加信息
LOG_FILE='ten.log'
LOG_LEVEL='INFO'
接下来执行:scrapy crawl tencent_crawl。或者 command line 命令行执行:
scrapy crawl tencent_crawl --logfile 'ten.log' -L INFO
输出如下
print http://hr.tencent.com/position.php?start=10
print http://hr.tencent.com/position.php?start=1340
print http://hr.tencent.com/position.php?start=0
print http://hr.tencent.com/position.php?start=1320
print http://hr.tencent.com/position.php?start=1310
print http://hr.tencent.com/position.php?start=1300
print http://hr.tencent.com/position.php?start=1290
print http://hr.tencent.com/position.php?start=1260
ten.log 文件中记录,可以看到级别大于 INFO 日志输出
2016-08-15 23:10:57 [tencent_crawl] INFO: info on http://hr.tencent.com/position.php?start=70
2016-08-15 23:10:57 [tencent_crawl] WARNING: WARNING on http://hr.tencent.com/position.php?start=70
2016-08-15 23:10:57 [tencent_crawl] ERROR: info on http://hr.tencent.com/position.php?start=70
2016-08-15 23:10:57 [tencent_crawl] INFO: info on http://hr.tencent.com/position.php?start=1320
2016-08-15 23:10:57 [tencent_crawl] WARNING: WARNING on http://hr.tencent.com/position.php?start=1320
2016-08-15 23:10:57 [tencent_crawl] ERROR: info on http://hr.tencent.com/position.php?start=1320
案例(二)( logging.getLogger )
tencent_spider.py 添加日志信息如下:logger = logging.getLogger('zhangsan')
'''
添加日志信息
'''
print 'print',response.url
self.logger.info('info on %s', response.url)
self.logger.warning('WARNING on %s', response.url)
self.logger.debug('info on %s', response.url)
self.logger.error('info on %s', response.url)
完整版如下:
import scrapy
from tutorial.items import RecruitItem
import re
import logging
logger = logging.getLogger('zhangsan')
class RecruitSpider(scrapy.spiders.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
#logger.info('spider tencent Parse function called on %s', response.url)
'''
添加日志信息
'''
print 'print',response.url
logger.info('info on %s', response.url)
logger.warning('WARNING on %s', response.url)
logger.debug('info on %s', response.url)
logger.error('info on %s', response.url)
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog =None
if sel.xpath('./td[2]/text()'):
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
#print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name
item['detailLink']=detailLink
if catalog:
item['catalog']=catalog
item['recruitNumber']=recruitNumber
item['workLocation']=workLocation
item['publishTime']=publishTime
yield item
nextFlag = response.xpath('//*[@id="next"]/@href')[0].extract()
if 'start' in nextFlag:
curpage = re.search('(\d+)',response.url).group(1)
page =int(curpage)+10
url = re.sub('\d+',str(page),response.url)
print url
yield scrapy.Request(url, callback=self.parse)
在 settings 文件中,修改添加信息
LOG_FILE='tencent.log'
LOG_LEVEL='WARNING'
接下来执行:scrapy crawl tencent 。或者command line命令行执行:
scrapy crawl tencent --logfile 'tencent.log' -L WARNING
输出信息
print http://hr.tencent.com/position.php?&start=0
http://hr.tencent.com/position.php?&start=10
print http://hr.tencent.com/position.php?&start=10
http://hr.tencent.com/position.php?&start=20
print http://hr.tencent.com/position.php?&start=20
http://hr.tencent.com/position.php?&start=30
tencent.log 文件中记录,可以看到级别大于 INFO 日志输出
2016-08-15 23:22:59 [zhangsan] WARNING: WARNING on http://hr.tencent.com/position.php?&start=0
2016-08-15 23:22:59 [zhangsan] ERROR: info on http://hr.tencent.com/position.php?&start=0
2016-08-15 23:22:59 [zhangsan] WARNING: WARNING on http://hr.tencent.com/position.php?&start=10
2016-08-15 23:22:59 [zhangsan] ERROR: info on http://hr.tencent.com/position.php?&start=10
小试 LOG_STDOUT
settings.py
LOG_FILE='tencent.log'
LOG_STDOUT=True
LOG_LEVEL='INFO'
执行:scrapy crawl tencent
输出:空
tencent.log 文件中记录,可以看到级别大于 INFO 日志输出
2016-08-15 23:28:32 [stdout] INFO: http://hr.tencent.com/position.php?&start=110
2016-08-15 23:28:32 [stdout] INFO: print
2016-08-15 23:28:32 [stdout] INFO: http://hr.tencent.com/position.php?&start=110
2016-08-15 23:28:32 [zhangsan] INFO: info on http://hr.tencent.com/position.php?&start=110
2016-08-15 23:28:32 [zhangsan] WARNING: WARNING on http://hr.tencent.com/position.php?&start=110
2016-08-15 23:28:32 [zhangsan] ERROR: info on http://hr.tencent.com/position.php?&start=110
2016-08-15 23:28:32 [stdout] INFO: http://hr.tencent.com/position.php?&start=120
2016-08-15 23:28:33 [stdout] INFO: print
2016-08-15 23:28:33 [stdout] INFO: http://hr.tencent.com/position.php?&start=120
2016-08-15 23:28:33 [zhangsan] INFO: info on http://hr.tencent.com/position.php?&start=120
2016-08-15 23:28:33 [zhangsan] WARNING: WARNING on http://hr.tencent.com/position.php?&start=120
2016-08-15 23:28:33 [zhangsan] ERROR: info on http://hr.tencent.com/position.php?&start=120
scrapy 之 Logging 使用
#coding:utf-8
######################
##Logging的使用
######################
import logging
'''
1. logging.CRITICAL - for critical errors (highest severity) 致命错误
2. logging.ERROR - for regular errors 一般错误
3. logging.WARNING - for warning messages 警告+错误
4. logging.INFO - for informational messages 消息+警告+错误
5. logging.DEBUG - for debugging messages (lowest severity) 低级别
'''
logging.warning("This is a warning")
logging.log(logging.WARNING,"This is a warning")
#获取实例对象
logger=logging.getLogger()
logger.warning("这是警告消息")
#指定消息发出者
logger = logging.getLogger('SimilarFace')
logger.warning("This is a warning")
#在爬虫中使用log
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://scrapinghub.com']
def parse(self, response):
#方法1 自带的logger
self.logger.info('Parse function called on %s', response.url)
#方法2 自己定义个logger
logger.info('Parse function called on %s', response.url)
'''
Logging 设置
• LOG_FILE
• LOG_ENABLED
• LOG_ENCODING
• LOG_LEVEL
• LOG_FORMAT
• LOG_DATEFORMAT • LOG_STDOUT
命令行中使用
--logfile FILE
Overrides LOG_FILE
--loglevel/-L LEVEL
Overrides LOG_LEVEL
--nolog
Sets LOG_ENABLED to False
'''
import logging
from scrapy.utils.log import configure_logging
configure_logging(install_root_handler=False)
#定义了logging的些属性
logging.basicConfig(
filename='log.txt',
format='%(levelname)s: %(levelname)s: %(message)s',
level=logging.INFO
)
#运行时追加模式
logging.info('进入Log文件')
logger = logging.getLogger('SimilarFace')
logger.warning("也要进入Log文件")
6、Settings
Scrapy 设置(settings)提供了定制 Scrapy 组件的方法。可以控制包括核心(core),插件(extension),pipeline 及 spider 组件。比如 设置 Json Pipeliine、LOG_LEVEL
内置设置列表请参考内置设置参考手册
获取设置值 (Populating the settings)
设置可以通过多种方式设置,每个方式具有不同的优先级。
下面以 优先级降序 的方式给出方式列表:
- 命令行选项(Command line Options)(最高优先级) 。命令行传入的参数具有最高的优先级。 使用选项 -s (或 --set) 来覆盖一个 (或更多) 选项。比如:scrapy crawl myspider -s LOG_FILE=scrapy.log
- 每个 spider 的设置 ( scrapy.spiders.Spider.custom_settings )。
class MySpider(scrapy.Spider):
name = 'myspider'
custom_settings = {
'SOME_SETTING': 'some value',
}
- 项目设置模块 (Project settings module)。项目设置模块是 Scrapy 项目的标准配置文件。即 setting.py
myproject.settings
如何访问配置 (settings)
In a spider, the settings are available through self.settings:
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://example.com']
def parse(self, response):
print("Existing settings: %s" % self.settings.attributes.keys())
Settings can be accessed through the scrapy.crawler.Crawler.settings attribute of the Crawler that is passed to from_crawler method in extensions, middlewares and item pipelines:
class MyExtension(object):
def __init__(self, log_is_enabled=False):
if log_is_enabled:
print("log is enabled!")
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(settings.getbool('LOG_ENABLED'))
案例 ( self.settings 使用 )
添加一行代码 print("Existing settings: %s" % self.settings['LOG_FILE'])
# -*- coding:utf-8 -*-
import scrapy
from tutorial.items import RecruitItem
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
import logging
class RecruitSpider(CrawlSpider):
name = "tencent_crawl"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
#提取匹配 'http://hr.tencent.com/position.php?&start=\d+'的链接
page_lx = LinkExtractor(allow=('start=\d+'))
rules = [
#提取匹配,并使用spider的parse方法进行分析;并跟进链接(没有callback意味着follow默认为True)
Rule(page_lx, callback='parseContent',follow=True)
]
def parseContent(self, response):
print response.url
print("Existing settings: %s" % self.settings['LOG_FILE'])
self.logger.info('Parse function called on %s', response.url)
for sel in response.xpath('//*[@class="even"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog =None
if sel.xpath('./td[2]/text()'):
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
#print name, detailLink, catalog,recruitNumber,workLocation,publishTime
item = RecruitItem()
item['name']=name.encode('utf-8')
item['detailLink']=detailLink.encode('utf-8')
if catalog:
item['catalog']=catalog.encode('utf-8')
item['recruitNumber']=recruitNumber.encode('utf-8')
item['workLocation']=workLocation.encode('utf-8')
item['publishTime']=publishTime.encode('utf-8')
yield item
内置设置参考手册
BOT_NAME:默认: 'scrapybot'。当您使用 startproject 命令创建项目时其也被自动赋值。
CONCURRENT_ITEMS:默认: 100。Item Processor(即 Item Pipeline) 同时处理(每个response的)item的最大值。
CONCURRENT_REQUESTS:默认: 16。Scrapy downloader 并发请求(concurrent requests)的最大值。
DEFAULT_REQUEST_HEADERS 默认:
{
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
Scrapy HTTP Request使用的默认header。
DEPTH_LIMIT:默认: 0。爬取网站最大允许的深度(depth)值。如果为0,则没有限制。
DOWNLOAD_DELAY:默认: 0。下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力。同时也支持小数:
DOWNLOAD_DELAY = 0.25 # 250 ms of delay:
该设置影响(默认启用的) RANDOMIZE_DOWNLOAD_DELAY 设置。 默认情况下,Scrapy在两个请求间不等待一个固定的值, 而是使用0.5到1.5之间的一个随机值 * DOWNLOAD_DELAY 的结果作为等待间隔。
DOWNLOAD_TIMEOUT:默认: 180。下载器超时时间(单位: 秒)。
ITEM_PIPELINES:默认: {}。保存项目中启用的pipeline及其顺序的字典。该字典默认为空,值(value)任意。 不过值(value)习惯设置在0-1000范围内。
样例:
ITEM_PIPELINES = {
'mybot.pipelines.validate.ValidateMyItem': 300,
'mybot.pipelines.validate.StoreMyItem': 800,
}
LOG_ENABLED:默认: True。是否启用logging。
LOG_ENCODING:默认: 'utf-8'。logging使用的编码。
LOG_LEVEL:默认: 'DEBUG'。log的最低级别。可选的级别有: CRITICAL、 ERROR、WARNING、INFO、DEBUG 。
USER_AGENT:默认: "Scrapy/VERSION (+http://scrapy.org)"。爬取的默认User-Agent,除非被覆盖。
阳光热线问政平台( 东莞 )
目标网址:http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4
items.py:添加以下代码
from scrapy.item import Item, Field
class SunItem(Item):
number = Field()
url = Field()
title = Field()
content = Field()
在 spiders 目录下新建一个自定义 SunSpider.py
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
# from tutorial.items import SunItem
import scrapy
import urllib
import time
import re
class SunSpider(CrawlSpider):
name = 'sun0769'
num = 0
allow_domain = ['http://wz.sun0769.com/']
start_urls = [
'http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4'
]
rules = {
Rule(LinkExtractor(allow='page'), process_links='process_request', follow=True),
Rule(LinkExtractor(allow=r'/html/question/\d+/\d+\.shtml$'), callback='parse_content')
}
def process_request(self, links):
ret = []
for link in links:
try:
page = re.search(r'page=\d*', link.url).group()
tp = re.search(r'type=\d+', link.url).group()
link.url = 'http://wz.sun0769.com/index.php/question/questionType?' + page + "&" + tp
except BaseException as e:
print(e)
ret.append(link)
return ret
def parse_content(self, response):
item = SunItem()
url = response.url
title = response.xpath('//*[@class="greyframe"]/div/div/strong/text()')[0].extract().strip()
number = response.xpath(
'//*[@class="greyframe"]/div/div/strong/text()'
)[0].extract().strip().split(':')[-1]
content = response.xpath('//div[@class="c1 text14_2"]/text()').extract()[0].strip()
item['url'] = url
item['title'] = title
item['number'] = number
item['content'] = content
print(dict(item))
# yield item
if __name__ == '__main__':
from scrapy import cmdline
cmdline.execute('scrapy crawl sun0769'.split())
pass
在 pipelines.py:添加如下代码
import json
import codecs
class JsonWriterPipeline(object):
def __init__(self):
self.file = codecs.open('items.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
def spider_closed(self, spider):
self.file.close()
settings.py 添加如下代码(启用组件)
ITEM_PIPELINES = {
'tutorial.pipelines.JsonWriterPipeline': 300,
}
window 下调试
在项目根目录下新建 main.py 文件,用于调试
from scrapy import cmdline
cmdline.execute('scrapy crawl sun0769'.split())
7、部署、可视化 管理 爬虫
爬虫 可视化 管理平台:Gerapy、crawllab、crawlab-lite
爬虫可视化管理平台、爬虫可视化调度工具,分布式爬虫管理框架、scrapy 可视化调度工具
框架
技术
优点
缺点
Crawlab
Golang
Vue
不限于Scrapy,适用于所有编程语言和框架。漂亮的UI界面。自然地支持分布式蜘蛛。支持蜘蛛管理,任务管理,cron作业,结果导出,分析,通知,可配置蜘蛛,在线代码编辑器等。
暂不支持版本控制
ScrapydWeb
Python Flask
Vue
漂亮的UI界面,内置Scrapy日志解析器,任务执行的统计数据和图表,支持节点管理,cron作业,邮件通知,移动。全功能蜘蛛管理平台。
不支持除 Scrapy 以外的其他蜘蛛。由于后端Python Flask性能有限。
Gerapy
Python Django
Vue
Gerapy是由崔庆才创建的。安装部署简单。漂亮的UI界面。支持节点管理、代码编辑、可配置抓取规则等。
同样不支持除Scrapy以外的其他蜘蛛。根据用户反馈,1.0版有很多bug。期待v2.0中的改进
SpiderKeeper
Python Flask
开源Scrapyhub。简洁明了的UI界面。支持cron作业。
可能太简单了,不支持分页,不支持节点管理,不支持Scrapy以外的蜘蛛。
Gerapy、crawllab、crawlab-lite
- SpiderKeeper 可能是最早的爬虫管理平台,但功能相对来说比较局限;
- Gerapy 虽然功能齐全,界面精美,但有不少 bug 需要处理。
- Scrapydweb 是一个比较完善的爬虫管理平台,不过和前两者一样,都是基于 scrapyd 的,因此只能运行 scrapy 爬虫;
- Crawlab是一个非常灵活的爬虫管理平台,可以运行 Python、Nodejs、Java、PHP、Go 写的爬虫,而且功能比较齐全,只是部署起来相对于前三者来说要麻烦一些,不过对于 Docker 使用者来说可以做到一键部署。
大多数现有的平台都依赖于 Scrapyd,这将选择限制在 python 和 scrapy 之间。当然 scrapy 是一个很棒的网络抓取框架,但是它不能做所有的事情。
对于重度 scrapy 爬虫依赖的、又不想折腾的开发者,可以考虑 Scrapydweb;而对于有各种类型的、复杂技术结构的爬虫开发者来说,可以考虑更灵活的 Crawlab。当然,不是说 Crawlab 对 scrapy 支持不友好,Crawlab 同样可以很好的集成 scrapy,也很容易使用,足够通用,可以适应任何语言和框架中的蜘蛛。它还有一个漂亮的前端界面,用户可以更容易地管理蜘蛛。