Scrapy学习笔记(5)-CrawlSpider+sqlalchemy实战

前言

在之前的文章Scrapy学习笔记(2)-使用pycharm在虚拟环境中运行第一个spider中有提到在使用scrapy genspider命令生成spider的时候可以使用-t参数来指定生成spider的模板,前面几篇文章中我们没有指定模板,所以都是使用最基本的Spider类来爬取数据。Spider其实能做很多事情了,但是如果你想对某个网站进行全站爬取的话,你可能需要一个更强大的武器—CrawlSpider。另外之前的文章中我们如果要把爬取的数据保存到数据库,那我们就得提前在库中执行DDL语句将表建好,有时候觉得设计表并写SQL是个很麻烦的事情,就想着能不能省掉写SQL的过程,查了下资料还真有,那就是使用sqlalchemy,本文记录实战过程。

基础知识

class scrapy.spiders.CrawlSpider

这是抓取一般网页最常用的类,除了从Spider继承过来的属性外,其提供了一个新的属性rules,它提供了一种简单的机制,能够为将要爬取的链接定义一组提取规则。

rules

这是一个Rule对象列表,每条规则定义了爬取网站链接的行为,如果一条链接命中多条规则,以第一条规则进行匹配,顺序由属性中定义的顺序决定。

Link Extractors

Link Extractors 是用于从网页(scrapy.http.Response)中抽取会被follow链接的对象。 Scrapy 自带的Link Extractors类由scrapy.linkextractors模块提供,你可以这样直接导入from scrapy.linkextractors import LinkExtractor,也可以通过实现一个简单的接口来创建自己个性化的Link Extractor来满足需求。每个LinkExtractor都有唯一的公共方法是 extract_links ,其接收 一个Response对象, 并返回scrapy.link.Link 对象。 Link Extractors只实例化一次,其 extract_links 方法会根据不同的response被调用多次来提取链接。默认的link extractor 是 LinkExtractor ,其实就是 LxmlLinkExtractor,在以前版本的Scrapy版本中还提供了其他的link extractor,不过都已经被废弃了。

LxmlLinkExtractor

classscrapy.linkextractors.lxmlhtml.LxmlLinkExtractor(

allow=(),

deny=(),

allow_domains=(),

deny_domains=(),

deny_extensions=None,

restrict_xpaths=(),

restrict_css=(),

tags=(‘a’, ‘area’),

attrs=(‘href’, ),

canonicalize=True,

unique=True,

process_value=None

)

参数解释:

allow 只有匹配这个正则表达式(或正则表达式列表)的URL才会被提取。如果没有给出(或None) ,它会匹配所有的链接。

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

allow_domains 包含特定域名的字符串或字符串列表,表示允许从这里面提取链接

deny_domains 包含特定域名的字符串或字符串列表, 表示不允许从这里面提取链接

deny_extensions 提取链接时,忽略扩展名的列表。如果没有给出,默认为scrapy.linkextractor模块中定义的ignored_extensions列表。

restrict_xpaths 单个xpath表达式或xpath表达式列表,若不为空,则只使用该参数去提取URL,和allow共同作用过滤链接。

restrict_css 单个css选择器或者选择器列表,作用和restrict_xpaths一样

tags 提取链接时要考虑的标签或标签列表。默认为 ( ‘a’ , ‘area’)

attrs 提取链接时应该寻找的attrbitues列表(仅在 tags 参数中指定的标签)。默认为 (‘href’)。

canonicalize 是否标准化每个提取的URL,使用w3lib.url.canonicalize_url。默认为True。

unique 是否过滤提取过的URL,布尔类型

process_value 处理tags和attrs提取到的URL的函数,它能修改并返回一个新值。如果为空则默认是lambda x: x

Rule

classscrapy.spiders.Rule(

link_extractor,

callback=None,

cb_kwargs=None,

follow=None,

process_links=None,

process_request=None

)

参数解释:

link_extractor 是一个Link Extractor对象,定义怎样提取每个需要爬取的页面中的链接。

callback 是一个可调用方法或者一个字符串(spider类中用这个字符串命名的方法)会被每个指定的link_extractor 调用,这个方法的第一个参数是response必须返回一个item或者Request的list。

cb_kwargs 是一个包含关键字参数的字典,可以传递给callback函数。

follow 是一个布尔值,指定这些通过规则匹配出来的链接是否需要继续,如果callback是None,follow默认为False,否则follow是True。

process_links是一个可调用方法或者一个字符串(spider类中用这个字符串命名的方法)会被每个指定的link_extractor调用,这个主要作用是过滤。

process_request 是一个可调用方法或者一个字符串(spider类中用这个字符串命名的方法)会被这个规则的每个request调用,必须返回一个request或者None。

SQLAlchemy

是python的一款开源软件,提供了SQL工具包及对象关系映射(ORM)工具(需要安装第三方库)。它的优点用一句话概括就是可以避免写繁复的sql语句.(隐藏数据库,良好的数据接口,动态的数据映射,引入缓存)具体请参考官方文档

Xpath

scrapy支持使用xpath表达式来提取数据。XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。 XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初XPath的提出的初衷是将其作为一个通用的、介于XPointer与XSL间的语法模型。但是XPath很快的被开发者采用来当作小型查询语言。具体可参考http://www.runoob.com/xpath/xpath-tutorial.html

实践

有了前面的知识和基本概念之后,下面就是写代码了,本文目标是使用CrawlSpider和sqlalchemy实现如下网站中的高匿代理IP采集入库http://ip84.com,新建项目和spider的过程我就不写了,不会的可以参考之前的文章,本次项目名称为”ip_proxy_pool”,顾名思义就是IP代理池,学习爬虫的应该都知道,不过本文仅仅是采集特定网站公开的代理IP,维护一个IP代理池那是后话,OK,Talk is cheap,Show you the code!

项目结构如上图所示,model目录存放数据库表的映射文件,proxy.py是目标表的映射文件,rules.py以及和model目录同级的__init__.py文件本文中暂时用不到先不管,其他文件都是本次实践需要用到的。

1.items.py

# -*- coding: utf-8 -*-# Define here the models for your scraped items## See documentation in:# http://doc.scrapy.org/en/latest/topics/items.htmlimport scrapyclass IpProxyPoolItem(scrapy.Item):    ip_port = scrapy.Field()    type = scrapy.Field()    level = scrapy.Field()    country = scrapy.Field()    location = scrapy.Field()    speed = scrapy.Field()    source = scrapy.Field()

2.model目录下的__init__.py

# -*- coding: utf-8 -*-from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmaker# 创建对象的基类:Base = declarative_base()# 初始化数据库连接:engine = create_engine('mysql+mysqldb://root:123456@localhost:3306/scrapy?charset=utf8')#返回数据库会话def loadSession():    Session = sessionmaker(bind=engine)    session = Session()    return session

3.proxy.py(数据库表proxies的映射文件)

# -*- coding: utf-8 -*-from sqlalchemy import Column,String,Integer,DateTimefrom . import Baseimport datetimeclass Proxy(Base):    __tablename__ = 'proxies'    ip_port=Column(String(30),primary_key=True,nullable=False)    type=Column(String(20),nullable=True)    level=Column(String(20),nullable=True)    location=Column(String(100),nullable=True)    speed=Column(Integer,nullable=True)    source = Column(String(500), nullable=False)    indate=Column(DateTime,nullable=False)    def __init__(self,ip_port,source,type=None,level=None,location=None,speed=None):        self.ip_port=ip_port

        self.type=type

        self.level=level

        self.location=location

        self.speed=speed

        self.source=source

        self.indate=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

4.pipelines.py

# -*- coding: utf-8 -*-# Define your item pipelines herefrom model import Base,engine,loadSessionfrom model import proxyclass IpProxyPoolPipeline(object):    #搜索Base的所有子类,并在数据库中生成表    Base.metadata.create_all(engine)    def process_item(self, item, spider):        a = proxy.Proxy(            ip_port=item['ip_port'],            type=item['type'],            level=item['level'],            location=item['location'],            speed=item['speed'],            source=item['source']        )        session = loadSession()        session.add(a)        session.commit()        return item

5.proxy_spider.py

# -*- coding: utf-8 -*-import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom ..items import IpProxyPoolItemclass ProxySpiderSpider(CrawlSpider):    name = 'proxy_spider'    allowed_domains = ['ip84.com']    start_urls = ['http://ip84.com/gn']    rules = (        #跟随下一页链接        Rule(LinkExtractor(restrict_xpaths="//a[@class='next_page']"),follow=True),        #对所有链接中含有"/gn/数字"的链接调用parse_item函数进行数据提取并过滤重复链接        Rule(LinkExtractor(allow=r'/gn/\d+',unique=True), callback='parse_item'),    )    def parse_item(self, response):        print 'Hi, this is an item page! %s' % response.url

        item=IpProxyPoolItem()        for proxy in response.xpath("//table[@class='list']/tr[position()>1]"):            ip=proxy.xpath("td[1]/text()").extract_first()            port=proxy.xpath("td[2]/text()").extract_first()            location1=proxy.xpath("td[3]/a[1]/text()").extract_first()            location2=proxy.xpath("td[3]/a[2]/text()").extract_first()            level=proxy.xpath("td[4]/text()").extract_first()            type = proxy.xpath("td[5]/text()").extract_first()            speed=proxy.xpath("td[6]/text()").extract_first()            item['ip_port']=(ip if ip else "")+":"+(port if port else "")            item['type']=(type if type else "")            item['level']=(level if level else "")            item['location']=(location1 if location1 else "")+" "+(location2 if location2 else "")            item['speed']=(speed if speed else "")            item['source']=response.url

            return item

6.settings.py

# -*- coding: utf-8 -*-# Scrapy settings for ip_proxy_pool projectBOT_NAME = 'ip_proxy_pool'SPIDER_MODULES = ['ip_proxy_pool.spiders']NEWSPIDER_MODULE = 'ip_proxy_pool.spiders'# Obey robots.txt rulesROBOTSTXT_OBEY = TrueITEM_PIPELINES = {  'ip_proxy_pool.pipelines.IpProxyPoolPipeline': 300,}DOWNLOAD_DELAY = 2

7.运行spider,查看结果

shell中执行scrapy crawl proxy_spider,发现数据库中已经自动生成了表proxies并且数据已经入库,Done!

更多原创文章,尽在金笔头博客

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 12,587评论 4 46
  • scrapy是python最有名的爬虫框架之一,可以很方便的进行web抓取,并且提供了很强的定制型,这里记录简单学...
    bomo阅读 2,037评论 1 11
  • 进程监控脚本 背景 启动于正式服务器的项目,有时会因为某种因素,例如docker内存超限因而容器崩溃(前不久就遇过...
    Joryun阅读 3,302评论 2 12
  • 每个丢了孩子的父母,都像发了疯一样,恨不能手刃人拐卖者。也决计做不到释怀,静下心来过日子。 01 她是挑着两个床单...
    夏米乐阅读 749评论 10 7
  • 昙花一现 只为一香 风驰电掣 只为一舞 你拼命追风 为何 北方的天空已经苍黑 南方的天空却还安详 与狼共舞 此生无悔
    尔海阅读 186评论 2 0