Scrapy學(xué)習(xí)筆記(5)-CrawlSpider+sqlalchemy實(shí)戰(zhàn)

前言

在之前的文章Scrapy學(xué)習(xí)筆記(2)-使用pycharm在虛擬環(huán)境中運(yùn)行第一個(gè)spider中有提到在使用scrapy genspider命令生成spider的時(shí)候可以使用-t參數(shù)來指定生成spider的模板桩警,前面幾篇文章中我們沒有指定模板晕拆,所以都是使用最基本的Spider類來爬取數(shù)據(jù)胸哥。Spider其實(shí)能做很多事情了,但是如果你想對(duì)某個(gè)網(wǎng)站進(jìn)行全站爬取的話赴魁,你可能需要一個(gè)更強(qiáng)大的武器—CrawlSpider卸奉。另外之前的文章中我們?nèi)绻雅廊〉臄?shù)據(jù)保存到數(shù)據(jù)庫,那我們就得提前在庫中執(zhí)行DDL語句將表建好颖御,有時(shí)候覺得設(shè)計(jì)表并寫SQL是個(gè)很麻煩的事情榄棵,就想著能不能省掉寫SQL的過程,查了下資料還真有潘拱,那就是使用sqlalchemy疹鳄,本文記錄實(shí)戰(zhàn)過程。

基礎(chǔ)知識(shí)

class scrapy.spiders.CrawlSpider

這是抓取一般網(wǎng)頁最常用的類芦岂,除了從Spider繼承過來的屬性外瘪弓,其提供了一個(gè)新的屬性rules,它提供了一種簡(jiǎn)單的機(jī)制禽最,能夠?yàn)閷⒁廊〉逆溄佣x一組提取規(guī)則腺怯。

rules

這是一個(gè)Rule對(duì)象列表袱饭,每條規(guī)則定義了爬取網(wǎng)站鏈接的行為,如果一條鏈接命中多條規(guī)則呛占,以第一條規(guī)則進(jìn)行匹配虑乖,順序由屬性中定義的順序決定。

Link Extractors

Link Extractors 是用于從網(wǎng)頁(scrapy.http.Response)中抽取會(huì)被follow鏈接的對(duì)象晾虑。 Scrapy 自帶的Link Extractors類由scrapy.linkextractors模塊提供疹味,你可以這樣直接導(dǎo)入from scrapy.linkextractors?import?LinkExtractor,也可以通過實(shí)現(xiàn)一個(gè)簡(jiǎn)單的接口來創(chuàng)建自己個(gè)性化的Link Extractor來滿足需求走贪。每個(gè)LinkExtractor都有唯一的公共方法是?extract_links?佛猛,其接收 一個(gè)Response對(duì)象, 并返回scrapy.link.Link?對(duì)象? Link Extractors只實(shí)例化一次坠狡,其?extract_links?方法會(huì)根據(jù)不同的response被調(diào)用多次來提取鏈接?默認(rèn)的link extractor 是?LinkExtractor?,其實(shí)就是?LxmlLinkExtractor遂跟,在以前版本的Scrapy版本中還提供了其他的link extractor逃沿,不過都已經(jīng)被廢棄了。

LxmlLinkExtractor

classscrapy.linkextractors.lxmlhtml.LxmlLinkExtractor(

allow=(),

deny=(),

allow_domains=(),

deny_domains=(),

deny_extensions=None,

restrict_xpaths=(),

restrict_css=(),

tags=(‘a(chǎn)’,?‘a(chǎn)rea’),

attrs=(‘href’,?),

canonicalize=True,

unique=True,

process_value=None

)

參數(shù)解釋:

allow 只有匹配這個(gè)正則表達(dá)式(或正則表達(dá)式列表)的URL才會(huì)被提取?如果沒有給出(或None) ,它會(huì)匹配所有的鏈接?

deny 匹配這個(gè)正則表達(dá)式(或正則表達(dá)式列表)的URL將會(huì)被排除在外(即不提取)?它的優(yōu)先級(jí)高于allow參數(shù)幻锁,如果沒有給出(或None) ,將不排除任何鏈接?

allow_domains 包含特定域名的字符串或字符串列表凯亮,表示允許從這里面提取鏈接

deny_domains 包含特定域名的字符串或字符串列表, 表示不允許從這里面提取鏈接

deny_extensions 提取鏈接時(shí),忽略擴(kuò)展名的列表?如果沒有給出,默認(rèn)為scrapy.linkextractor模塊中定義的ignored_extensions列表?

restrict_xpaths 單個(gè)xpath表達(dá)式或xpath表達(dá)式列表,若不為空,則只使用該參數(shù)去提取URL哄尔,和allow共同作用過濾鏈接假消。

restrict_css 單個(gè)css選擇器或者選擇器列表,作用和restrict_xpaths一樣

tags 提取鏈接時(shí)要考慮的標(biāo)簽或標(biāo)簽列表?默認(rèn)為?(?‘a(chǎn)’?,?‘a(chǎn)rea’)

attrs?提取鏈接時(shí)應(yīng)該尋找的attrbitues列表(僅在?tags?參數(shù)中指定的標(biāo)簽)?默認(rèn)為?(‘href’)?

canonicalize 是否標(biāo)準(zhǔn)化每個(gè)提取的URL岭接,使用w3lib.url.canonicalize_url富拗。默認(rèn)為True。

unique 是否過濾提取過的URL鸣戴,布爾類型

process_value 處理tags和attrs提取到的URL的函數(shù)啃沪,它能修改并返回一個(gè)新值。如果為空則默認(rèn)是lambda?x:?x

Rule

classscrapy.spiders.Rule(

link_extractor,

callback=None,

cb_kwargs=None,

follow=None,

process_links=None,

process_request=None

)

參數(shù)解釋:

link_extractor 是一個(gè)Link Extractor對(duì)象窄锅,定義怎樣提取每個(gè)需要爬取的頁面中的鏈接创千。

callback 是一個(gè)可調(diào)用方法或者一個(gè)字符串(spider類中用這個(gè)字符串命名的方法)會(huì)被每個(gè)指定的link_extractor 調(diào)用,這個(gè)方法的第一個(gè)參數(shù)是response必須返回一個(gè)item或者Request的list入偷。

cb_kwargs 是一個(gè)包含關(guān)鍵字參數(shù)的字典追驴,可以傳遞給callback函數(shù)。

follow 是一個(gè)布爾值疏之,指定這些通過規(guī)則匹配出來的鏈接是否需要繼續(xù)殿雪,如果callback是None,follow默認(rèn)為False体捏,否則follow是True冠摄。

process_links是一個(gè)可調(diào)用方法或者一個(gè)字符串(spider類中用這個(gè)字符串命名的方法)會(huì)被每個(gè)指定的link_extractor調(diào)用糯崎,這個(gè)主要作用是過濾。

process_request 是一個(gè)可調(diào)用方法或者一個(gè)字符串(spider類中用這個(gè)字符串命名的方法)會(huì)被這個(gè)規(guī)則的每個(gè)request調(diào)用河泳,必須返回一個(gè)request或者None沃呢。

SQLAlchemy

是python的一款開源軟件,提供了SQL工具包及對(duì)象關(guān)系映射(ORM)工具(需要安裝第三方庫)拆挥。它的優(yōu)點(diǎn)用一句話概括就是可以避免寫繁復(fù)的sql語句.(隱藏?cái)?shù)據(jù)庫薄霜,良好的數(shù)據(jù)接口,動(dòng)態(tài)的數(shù)據(jù)映射纸兔,引入緩存)具體請(qǐng)參考官方文檔

Xpath

scrapy支持使用xpath表達(dá)式來提取數(shù)據(jù)惰瓜。XPath即為XML路徑語言(XML?Path Language),它是一種用來確定XML文檔中某部分位置的語言汉矿。 XPath基于XML的樹狀結(jié)構(gòu)崎坊,提供在數(shù)據(jù)結(jié)構(gòu)樹中找尋節(jié)點(diǎn)的能力。起初XPath的提出的初衷是將其作為一個(gè)通用的洲拇、介于XPointer與XSL間的語法模型奈揍。但是XPath很快的被開發(fā)者采用來當(dāng)作小型查詢語言。具體可參考http://www.runoob.com/xpath/xpath-tutorial.html

實(shí)踐

有了前面的知識(shí)和基本概念之后赋续,下面就是寫代碼了男翰,本文目標(biāo)是使用CrawlSpider和sqlalchemy實(shí)現(xiàn)如下網(wǎng)站中的高匿代理IP采集入庫http://ip84.com,新建項(xiàng)目和spider的過程我就不寫了纽乱,不會(huì)的可以參考之前的文章蛾绎,本次項(xiàng)目名稱為”ip_proxy_pool”,顧名思義就是IP代理池鸦列,學(xué)習(xí)爬蟲的應(yīng)該都知道租冠,不過本文僅僅是采集特定網(wǎng)站公開的代理IP,維護(hù)一個(gè)IP代理池那是后話敛熬,OK肺稀,Talk is cheap,Show you the code应民!

項(xiàng)目結(jié)構(gòu)如上圖所示话原,model目錄存放數(shù)據(jù)庫表的映射文件,proxy.py是目標(biāo)表的映射文件诲锹,rules.py以及和model目錄同級(jí)的__init__.py文件本文中暫時(shí)用不到先不管繁仁,其他文件都是本次實(shí)踐需要用到的。

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# 創(chuàng)建對(duì)象的基類:Base = declarative_base()# 初始化數(shù)據(jù)庫連接:engine = create_engine('mysql+mysqldb://root:123456@localhost:3306/scrapy?charset=utf8')#返回?cái)?shù)據(jù)庫會(huì)話def loadSession():? ? Session = sessionmaker(bind=engine)? ? session = Session()? ? return session

3.proxy.py(數(shù)據(jù)庫表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的所有子類归园,并在數(shù)據(jù)庫中生成表? ? 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="http://a[@class='next_page']"),follow=True),? ? ? ? #對(duì)所有鏈接中含有"/gn/數(shù)字"的鏈接調(diào)用parse_item函數(shù)進(jìn)行數(shù)據(jù)提取并過濾重復(fù)鏈接? ? ? ? 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("http://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.運(yùn)行spider黄虱,查看結(jié)果

shell中執(zhí)行scrapy crawl proxy_spider,發(fā)現(xiàn)數(shù)據(jù)庫中已經(jīng)自動(dòng)生成了表proxies并且數(shù)據(jù)已經(jīng)入庫,Done!

更多原創(chuàng)文章庸诱,盡在金筆頭博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捻浦,一起剝皮案震驚了整個(gè)濱河市晤揣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朱灿,老刑警劉巖昧识,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異盗扒,居然都是意外死亡跪楞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門侣灶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甸祭,“玉大人,你說我怎么就攤上這事褥影〕鼗В” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵凡怎,是天一觀的道長(zhǎng)煞檩。 經(jīng)常有香客問我,道長(zhǎng)栅贴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任熏迹,我火速辦了婚禮檐薯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘注暗。我一直安慰自己坛缕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布捆昏。 她就那樣靜靜地躺著赚楚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骗卜。 梳的紋絲不亂的頭發(fā)上宠页,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音寇仓,去河邊找鬼举户。 笑死,一個(gè)胖子當(dāng)著我的面吹牛遍烦,可吹牛的內(nèi)容都是我干的俭嘁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼服猪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼供填!你這毒婦竟也來了拐云?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤近她,失蹤者是張志新(化名)和其女友劉穎叉瘩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泄私,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡房揭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晌端。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捅暴。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咧纠,靈堂內(nèi)的尸體忽然破棺而出蓬痒,到底是詐尸還是另有隱情,我是刑警寧澤漆羔,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布梧奢,位于F島的核電站,受9級(jí)特大地震影響演痒,放射性物質(zhì)發(fā)生泄漏亲轨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一鸟顺、第九天 我趴在偏房一處隱蔽的房頂上張望惦蚊。 院中可真熱鬧,春花似錦讯嫂、人聲如沸蹦锋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽莉掂。三九已至,卻和暖如春千扔,著一層夾襖步出監(jiān)牢的瞬間憎妙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工昏鹃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尚氛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓洞渤,卻偏偏與公主長(zhǎng)得像阅嘶,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,696評(píng)論 4 46
  • scrapy是python最有名的爬蟲框架之一讯柔,可以很方便的進(jìn)行web抓取抡蛙,并且提供了很強(qiáng)的定制型,這里記錄簡(jiǎn)單學(xué)...
    bomo閱讀 2,115評(píng)論 1 11
  • 進(jìn)程監(jiān)控腳本 背景 啟動(dòng)于正式服務(wù)器的項(xiàng)目魂迄,有時(shí)會(huì)因?yàn)槟撤N因素粗截,例如docker內(nèi)存超限因而容器崩潰(前不久就遇過...
    Joryun閱讀 3,364評(píng)論 2 12
  • 每個(gè)丟了孩子的父母熊昌,都像發(fā)了瘋一樣,恨不能手刃人拐賣者湿酸。也決計(jì)做不到釋懷婿屹,靜下心來過日子。 01 她是挑著兩個(gè)床單...
    夏米樂閱讀 801評(píng)論 10 7
  • 曇花一現(xiàn) 只為一香 風(fēng)馳電掣 只為一舞 你拼命追風(fēng) 為何 北方的天空已經(jīng)蒼黑 南方的天空卻還安詳 與狼共舞 此生無悔
    爾海閱讀 203評(píng)論 2 0