記得n年前項(xiàng)目需要一個(gè)靈活的爬蟲(chóng)工具,,就組織了一個(gè)小團(tuán)隊(duì)用Java實(shí)現(xiàn)了一個(gè)爬蟲(chóng)框架,可以根據(jù)目標(biāo)網(wǎng)站的結(jié)構(gòu),、地址和需要的內(nèi)容,,做簡(jiǎn)單的配置開(kāi)發(fā),即可實(shí)現(xiàn)特定網(wǎng)站的爬蟲(chóng)功能,。因?yàn)橐紤]到各種特殊情形,,開(kāi)發(fā)還耗了不少人力。后來(lái)發(fā)現(xiàn)了Python下有這個(gè)Scrapy工具,,瞬間覺(jué)得之前做的事情都白費(fèi)了,。對(duì)于一個(gè)普通的網(wǎng)絡(luò)爬蟲(chóng)功能,Scrapy完全勝任,,并把很多復(fù)雜的編程都包裝好了,。本文會(huì)介紹如何Scrapy構(gòu)建一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)爬蟲(chóng)。 一個(gè)基本的爬蟲(chóng)工具,,它應(yīng)該具備以下幾個(gè)功能: - 通過(guò)HTTP(S)請(qǐng)求,,下載網(wǎng)頁(yè)信息
- 解析網(wǎng)頁(yè),抓取需要的內(nèi)容
- 保存內(nèi)容
- 從現(xiàn)有頁(yè)面中找到有效鏈接,,從而繼續(xù)抓取下一個(gè)網(wǎng)頁(yè)
我們來(lái)看下Scrapy怎么做到這些功能的,。首先準(zhǔn)備Scrapy環(huán)境,你需要安裝Python(本文使用v2.7)和pip,,然后用pip來(lái)安裝lxml和scrapy,。個(gè)人強(qiáng)烈建議使用virtualenv來(lái)安裝環(huán)境,這樣不同的項(xiàng)目之間不會(huì)沖突,。詳細(xì)步驟這里就不贅述了,。對(duì)于Mac用戶要注意,當(dāng)使用pip安裝lxml時(shí),,會(huì)出現(xiàn)類似于的下面錯(cuò)誤: Error: #include “xml/xmlversion.h” not found
解決這個(gè)問(wèn)題,,你需要先安裝Xcode的command line tools,具體的方法是在命令行執(zhí)行下面的命令即可,。 環(huán)境安裝好之后,,我們來(lái)用Scrapy實(shí)現(xiàn)一個(gè)簡(jiǎn)單的爬蟲(chóng),抓取本博客網(wǎng)站的文章標(biāo)題,,地址和摘要,。 - 創(chuàng)建工程
| $ scrapy startproject my_crawler | 該命令會(huì)在當(dāng)前目錄下創(chuàng)建一個(gè)名為”my_crawler”的工程,,工程的目錄結(jié)構(gòu)如下 | my_crawler |- my_crawler | |- spiders | | |- __init__.py | |- items.py | |- pipelines.py | |- setting.py |- scrapy.cfg | - 設(shè)置待抓取內(nèi)容的字段,本例中就是文章的標(biāo)題,,地址和摘要
修改”items.py”文件,,在”MyCrawlerItem”類中加上如下代碼: | # -*- coding: utf-8 -*- import scrapy class MyCrawlerItem(scrapy.Item): title = scrapy.Field() # 文章標(biāo)題 url = scrapy.Field() # 文章地址 summary = scrapy.Field() # 文章摘要 pass | - 編寫網(wǎng)頁(yè)解析代碼
在”my_crawler/spiders”目錄下,創(chuàng)建一個(gè)名為”crawl_spider.py”文件(文件名可以任意?。?。代碼如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from my_crawler.items import MyCrawlerItem class MyCrawlSpider(CrawlSpider): name = 'my_crawler' # Spider名,必須唯一,,執(zhí)行爬蟲(chóng)命令時(shí)使用 allowed_domains = [''] # 限定允許爬的域名,,可設(shè)置多個(gè) start_urls = [ 'http://www.', # 種子URL,可設(shè)置多個(gè) ] rules = ( # 對(duì)應(yīng)特定URL,,設(shè)置解析函數(shù),,可設(shè)置多個(gè) Rule(LinkExtractor(allow=r'/page/[0-9]+'), # 指定允許繼續(xù)爬取的URL格式,支持正則 callback='parse_item', # 用于解析網(wǎng)頁(yè)的回調(diào)函數(shù)名 follow=True ), ) def parse_item(self, response): # 通過(guò)XPath獲取Dom元素 articles = response.xpath('//*[@id='main']/ul/li') for article in articles: item = MyCrawlerItem() item['title'] = article.xpath('h3[@class='entry-title']/a/text()').extract()[0] item['url'] = article.xpath('h3[@class='entry-title']/a/@href').extract()[0] item['summary'] = article.xpath('div[2]/p/text()').extract()[0] yield item |
對(duì)于XPath不熟悉的朋友,,可以通過(guò)Chrome的debug工具獲取元素的XPath。 - 讓我們測(cè)試下爬蟲(chóng)的效果
在命令行中輸入: | $ scrapy crawl my_crawler | 注意,,這里的”my_crawler”就是你在”crawl_spider.py”文件中起的Spider名,。 沒(méi)過(guò)幾秒鐘,你就會(huì)看到要抓取的字段內(nèi)容打印在控制臺(tái)上了,。就是這么神奇,!Scrapy將HTTP(S)請(qǐng)求,內(nèi)容下載,,待抓取和已抓取的URL隊(duì)列的管理都封裝好了,。你的主要工作基本上就是設(shè)置URL規(guī)則及編寫解析的方法。 我們將抓取的內(nèi)容保存為JSON文件: | $ scrapy crawl my_crawler -o my_crawler.json -t json | 你可以在當(dāng)前目錄下,,找到文件”my_crawler.json”,,里面保存的就是我們要抓取的字段信息。(參數(shù)”-t json”可以省去) - 將結(jié)果保存到數(shù)據(jù)庫(kù)
這里我們采用MongoDB,,你需要先安裝Python的MongoDB庫(kù)”pymongo”,。編輯”my_crawler”目錄下的”pipelines.py”文件,在”MyCrawlerPipeline”類中加上如下代碼: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # -*- coding: utf-8 -*- import pymongo from scrapy.conf import settings from scrapy.exceptions import DropItem class MyCrawlerPipeline(object): def __init__(self): # 設(shè)置MongoDB連接 connection = pymongo.Connection( settings['MONGO_SERVER'], settings['MONGO_PORT'] ) db = connection[settings['MONGO_DB']] self.collection = db[settings['MONGO_COLLECTION']] # 處理每個(gè)被抓取的MyCrawlerItem項(xiàng) def process_item(self, item, spider): valid = True for data in item: if not data: # 過(guò)濾掉存在空字段的項(xiàng) valid = False raise DropItem('Missing {0}!'.format(data)) if valid: # 也可以用self.collection.insert(dict(item)),,使用upsert可以防止重復(fù)項(xiàng) self.collection.update({'url': item['url']}, dict(item), upsert=True) return item | 再打開(kāi)”my_crawler”目錄下的”settings.py”文件,,在文件末尾加上pipeline的設(shè)置: | ITEM_PIPELINES = { 'my_crawler.pipelines.MyCrawlerPipeline': 300, # 設(shè)置Pipeline,可以多個(gè),,值為執(zhí)行優(yōu)先級(jí) } # MongoDB連接信息 MONGO_SERVER = 'localhost' MONGO_PORT = 27017 MONGO_DB = 'bjhee' MONGO_COLLECTION = 'articles' DOWNLOAD_DELAY=2 # 如果網(wǎng)絡(luò)慢,,可以適當(dāng)加些延遲,單位是秒 | - 執(zhí)行爬蟲(chóng)
| $ scrapy crawl my_crawler | 別忘了啟動(dòng)MongoDB并創(chuàng)建”bjhee”數(shù)據(jù)庫(kù)哦?,F(xiàn)在你可以在MongoDB里查詢到記錄了,。 總結(jié)下,,使用Scrapy來(lái)構(gòu)建一個(gè)網(wǎng)絡(luò)爬蟲(chóng),你需要做的就是: - “items.py”中定義爬取字段
- 在”spiders”目錄下創(chuàng)建你的爬蟲(chóng),,編寫解析函數(shù)和規(guī)則
- “pipelines.py”中對(duì)爬取后的結(jié)果做處理
- “settings.py”設(shè)置必要的參數(shù)
其他的事情,,Scrapy都幫你做了。下圖就是Scrapy具體工作的流程,。怎么樣,?開(kāi)始寫一個(gè)自己的爬蟲(chóng)吧。 本例中的代碼可以在這里下載,。
|