惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recorded Future
Recorded Future
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Scott Helme
Scott Helme
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
Google Online Security Blog
Google Online Security Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
WordPress大学
WordPress大学
博客园 - 聂微东
L
LINUX DO - 最新话题
月光博客
月光博客
小众软件
小众软件
T
Troy Hunt's Blog
A
Arctic Wolf
量子位
I
Intezer
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
Schneier on Security
Schneier on Security
Schneier on Security
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
有赞技术团队
有赞技术团队
N
News and Events Feed by Topic
美团技术团队
The Cloudflare Blog
P
Privacy International News Feed
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
GRAHAM CLULEY
N
News | PayPal Newsroom
Apple Machine Learning Research
Apple Machine Learning Research
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
V
Vulnerabilities – Threatpost
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
Application and Cybersecurity Blog
Application and Cybersecurity Blog
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
雷峰网
雷峰网

Jark's Blog

Flink 1.16:Hive SQL 如何平迁到 Flink SQL Flink CDC 如何简化实时数据入湖入仓 基于 Flink SQL 构建流批一体的 ETL 数据集成 Nexmark: 如何设计一个流计算基准测试? Demo:基于 Flink SQL 构建流式应用 Flink 1.9 实战:使用 SQL 读取 Kafka 并写入 MySQL Flink SQL 编程实践 如何从小白成长为 Apache Committer? 聊聊Blink开源和Flink社区近况 Flink 小贴士 (7): 4个步骤,让 Flink 应用达到生产状态 Flink 小贴士 (6): 使用 Broadcast State 的 4 个注意事项 Flink 小贴士 (5): Savepoint 和 Checkpoint 的 3 个不同点 Flink 小贴士 (4): 如何选择状态后端 Flink 小贴士 (3): 轻松理解 Watermark 一文了解 Apache Flink 核心技术 Flink 零基础实战教程:如何计算实时热门商品 5分钟从零构建第一个 Flink 应用 Flink 小贴士 (2):Flink 如何管理 Kafka 消费位点 Flink小贴士 (1):确定Flink作业所需资源大小时要考虑的6件事 Flink在美团的实践与应用 我在阿里的这两年 Flink 原理与实现:Aysnc I/O Flink 原理与实现:Table & SQL API Flink 原理与实现:Session Window Flink 原理与实现:Window 机制 Flink 原理与实现:数据流上的类型和操作 Flink 原理与实现:如何生成 JobGraph Flink 原理与实现:理解 Flink 中的计算资源 Flink 原理与实现:如何生成 StreamGraph Flink 原理与实现:架构和拓扑概览 Flink 原理与实现:内存管理 Flink 原理与实现:如何处理反压问题 Flink官方文档翻译:安装部署(集群模式) Flink官方文档翻译:安装部署(本地模式) 迟到的2015年终总结 高效Macbook开发之道(工具篇) Clojure学习笔记(三):并发与引用 Clojure学习笔记(二):语法 Clojure学习笔记(一):数据结构 Thrift 实践 Thrift 入门 Metrics 是个什么鬼 之入门教程 使用Redis和SQLAlchemy对Scrapy Item去重并存储 编程方式下运行 Scrapy spider 读《程序员必读的职业规划书》 Spark 下操作 HBase(1.0.0 新 API) HBase 集群安装部署 Spark On YARN 集群安装部署 Git 常用技能
使用Scrapy定制可动态配置的爬虫
WuChong · 2015-05-22 · via Jark's Blog

发表于   |   分类于 程序设计   |  

本文紧接上篇博客,在上一篇博客中我们讲解了如何使用编程的方式运行Scrapy spider。本文将讲解如何通过维护多个网站的爬取规则来抓取各个网站的数据。

具体要实现的目标是这样的,有一张Rule表用来存储各个网站的爬取规则,Scrapy获取Rule表中的记录后,针对每一条rule自动生成一个spider,每个spider去爬它们各自网站的数据。这样我们只需要维护Rule表中的规则(可以写个Web程序来维护),而不用针对上千个网站写上千个spider文件了。

我们使用 SQLAlchemy 来映射数据库,Rule表的结构如下:

from sqlalchemy import Column, String , DateTime, Integer
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Rule(Base):
__tablename__ = 'rules'

id = Column(Integer, primary_key=True)
name = Column(String)
allow_domains = Column(String)
start_urls = Column(String)
next_page = Column(String)
allow_url = Column(String)
extract_from = Column(String)
title_xpath = Column(String)
body_xpath = Column(String)
publish_time_xpath = Column(String)
source_site_xpath = Column(String)
enable = Column(Integer)

接下来我们要重新定制我们的spider,命名为DeepSpider,让他能够通过rule参数初始化。我们令DeepSpider继承自 CrawlSpider,一个提供了更多强大的规则(rule)来提供跟进link功能的类。deep_spider.py长这个样子:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor

class Article(scrapy.Item):
title = scrapy.Field()
url = scrapy.Field()
body = scrapy.Field()
publish_time = scrapy.Field()
source_site = scrapy.Field()

class DeepSpider(CrawlSpider):
name = "Deep"

def __init__(self,rule):
self.rule = rule
self.name = rule.name
self.allowed_domains = rule.allow_domains.split(",")
self.start_urls = rule.start_urls.split(",")
rule_list = []
#添加`下一页`的规则
if rule.next_page:
rule_list.append(Rule(LinkExtractor(restrict_xpaths = rule.next_page)))
#添加抽取文章链接的规则
rule_list.append(Rule(LinkExtractor(
allow=[rule.allow_url],
restrict_xpaths = [rule.extract_from]),
callback='parse_item'))
self.rules = tuple(rule_list)
super(DeepSpider, self).__init__()


def parse_item(self, response):
self.log('Hi, this is an article page! %s' % response.url)

article = Article()

article["url"] = response.url

title = response.xpath(self.rule.title_xpath).extract()
article["title"] = title[0] if title else ""

body = response.xpath(self.rule.body_xpath).extract()
article["body"] = '\n'.join(body) if body else ""

publish_time = response.xpath(self.rule.publish_time_xpath).extract()
article["publish_time"] = publish_time[0] if publish_time else ""

source_site = response.xpath(self.rule.source_site_xpath).extract()
article["source_site"] = source_site[0] if source_site else ""

return article

要注意的是start_urlsrules等都初始化成了对象的属性,都由传入的rule对象初始化,parse_item方法中的抽取规则也都有rule对象提供。

为了同时运行多个spider,我们需要稍稍修改上节中的运行脚本run.py,如下所示:

# -*- coding: utf-8 -*-
from spiders.deep_spider import DeepSpider
from model.config import DBSession
from model.rule import Rule

# scrapy api
from scrapy import signals, log
from twisted.internet import reactor
from scrapy.crawler import Crawler
from scrapy.settings import Settings


RUNNING_CRAWLERS = []

def spider_closing(spider):
"""Activates on spider closed signal"""
log.msg("Spider closed: %s" % spider, level=log.INFO)
RUNNING_CRAWLERS.remove(spider)
if not RUNNING_CRAWLERS:
reactor.stop()

log.start(loglevel=log.DEBUG)

settings = Settings()

# crawl settings
settings.set("USER_AGENT", "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36")

db = DBSession()
rules = db.query(Rule).filter(Rule.enable == 1)
for rule in rules:
crawler = Crawler(settings)
spider = DeepSpider(rule) # instantiate every spider using rule
RUNNING_CRAWLERS.append(spider)

# stop reactor when spider closes
crawler.signals.connect(spider_closing, signal=signals.spider_closed)
crawler.configure()
crawler.crawl(spider)
crawler.start()

# blocks process so always keep as the last statement
reactor.run()

我们从数据库中查出启用的rules,并对于rules中每一个规则实例化一个DeepSpider对象。这儿的一个小技巧是建立了一个RUNNING_CRAWLERS列表,新建立的DeepSpider对象 spider 都会加入这个队列。在 spider 运行完毕时会调用spider_closing方法,并将该spider从RUNNING_CRAWLERS移除。最终,RUNNING_CRAWLERS中没有任何spider了,我们会停止脚本。

运行run.py后,就能对Rule表中网站进行爬取了,但是我们现在还没有对爬下来的结果进行存储,所以看不到结果。下一篇博客,我们将使用 Scrapy 提供的强大的 Pipline 对数据进行保存并去重。

现在我们可以往Rule表中加入成百上千个网站的规则,而不用添加一行代码,就可以对这成百上千个网站进行爬取。当然你完全可以做一个Web前端来完成维护Rule表的任务。当然Rule规则也可以放在除了数据库的任何地方,比如配置文件。

由于本人刚接触 Scrapy 不久,如有理解不当之处或是更好的解决方案,还请不吝赐教 :)

你可以在 GitHub 上看到本文的完整项目。

注:本文使用的 Scrapy 版本是 0.24,GitHub 上的master分支已支持 Scrapy 1.0

本系列的三篇文章

  1. 编程方式下运行 Scrapy spider
  2. 使用Scrapy定制可动态配置的爬虫
  3. 使用Redis和SQLAlchemy对Scrapy Item去重并存储

###参考资料


hoxis wechat