最近一段時(shí)間,今日頭條各種推送python相關(guān)的文檔,什么“python都要加入高考了,再不學(xué)就out了”等等特別火熱,正好公司領(lǐng)導(dǎo)安排我去爬取一些網(wǎng)站新聞信息,可以趁著這個(gè)機(jī)會(huì)學(xué)習(xí)學(xué)習(xí)python,所以就決定用python來(lái)完成本次的爬取工作。因?yàn)橹皼](méi)有接觸過(guò)python,所以直接上手遇到了各種各樣的問(wèn)題,還好最后都解決了。
Scrapy簡(jiǎn)介
Scrapy是Python開(kāi)發(fā)的一個(gè)快速,高層次的屏幕抓取和Web抓取框架,用于抓取Web站點(diǎn)并從頁(yè)面中提取結(jié)構(gòu)化的數(shù)據(jù)。
下圖展示了Scrapy的大致架構(gòu),其中包含了主要組件和系統(tǒng)的數(shù)據(jù)處理流程(綠色箭頭表示)。下面會(huì)對(duì)組件和流程進(jìn)行了一個(gè)簡(jiǎn)單的解釋。

組件
1.Scrapy Engine(Scrapy引擎)
Scrapy引擎是用來(lái)控制整個(gè)系統(tǒng)的數(shù)據(jù)處理流程,并進(jìn)行事務(wù)處理的觸發(fā)。更多的詳細(xì)內(nèi)容可以看下面的數(shù)據(jù)處理流程。
2.Scheduler(調(diào)度程序)
調(diào)度程序從Scrapy引擎接受請(qǐng)求并排序列入隊(duì)列,并在Scrapy引擎發(fā)出請(qǐng)求后返還給它們。
3.Downloader(下載器)
下載器的主要職責(zé)是抓取網(wǎng)頁(yè)并將網(wǎng)頁(yè)內(nèi)容返還給蜘蛛(Spiders)。
4.Spiders(蜘蛛)
蜘蛛是有Scrapy用戶自己定義用來(lái)解析網(wǎng)頁(yè)并抓取制定URL返回的內(nèi)容的類,每個(gè)蜘蛛都能處理一個(gè)域名或一組域名。換句話說(shuō)就是用來(lái)定義特定網(wǎng)站的抓取和解析規(guī)則。
5.Item Pipeline(項(xiàng)目管道)
項(xiàng)目管道的主要責(zé)任是負(fù)責(zé)處理有蜘蛛從網(wǎng)頁(yè)中抽取的項(xiàng)目,它的主要任務(wù)是清晰、驗(yàn)證和存儲(chǔ)數(shù)據(jù)。當(dāng)頁(yè)面被蜘蛛解析后,將被發(fā)送到項(xiàng)目管道,并經(jīng)過(guò)幾個(gè)特定的次序處理數(shù)據(jù)。每個(gè)項(xiàng)目管道的組件都是有一個(gè)簡(jiǎn)單的方法組成的Python類。它們獲取了項(xiàng)目并執(zhí)行它們的方法,同時(shí)還需要確定的是是否需要在項(xiàng)目管道中繼續(xù)執(zhí)行下一步或是直接丟棄掉不處理。
項(xiàng)目管道通常執(zhí)行的過(guò)程有:
清洗HTML數(shù)據(jù) 驗(yàn)證解析到的數(shù)據(jù)(檢查項(xiàng)目是否包含必要的字段) 檢查是否是重復(fù)數(shù)據(jù)(如果重復(fù)就刪除) 將解析到的數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù)中
6.Middlewares(中間件)
中間件是介于Scrapy引擎和其他組件之間的一個(gè)鉤子框架,主要是為了提供一個(gè)自定義的代碼來(lái)拓展Scrapy的功能。
數(shù)據(jù)處理流程
Scrapy的整個(gè)數(shù)據(jù)處理流程有Scrapy引擎進(jìn)行控制,其主要的運(yùn)行方式為:
1.引擎打開(kāi)一個(gè)域名,蜘蛛處理這個(gè)域名,并讓蜘蛛獲取第一個(gè)爬取的URL。
2.引擎從蜘蛛那獲取第一個(gè)需要爬取的URL,然后作為請(qǐng)求在調(diào)度中進(jìn)行調(diào)度。
3.引擎從調(diào)度那獲取接下來(lái)進(jìn)行爬取的頁(yè)面。
4.調(diào)度將下一個(gè)爬取的URL返回給引擎,引擎將它們通過(guò)下載中間件發(fā)送到下載器。
5.當(dāng)網(wǎng)頁(yè)被下載器下載完成以后,響應(yīng)內(nèi)容通過(guò)下載中間件被發(fā)送到引擎。
6.引擎收到下載器的響應(yīng)并將它通過(guò)蜘蛛中間件發(fā)送到蜘蛛進(jìn)行處理。
7.蜘蛛處理響應(yīng)并返回爬取到的項(xiàng)目,然后給引擎發(fā)送新的請(qǐng)求。
8.引擎將抓取到的項(xiàng)目項(xiàng)目管道,并向調(diào)度發(fā)送請(qǐng)求。
9.系統(tǒng)重復(fù)第二部后面的操作,直到調(diào)度中沒(méi)有請(qǐng)求,然后斷開(kāi)引擎與域之間的聯(lián)系。
以上來(lái)自網(wǎng)絡(luò),現(xiàn)在開(kāi)始真正的開(kāi)發(fā):
新建工程:
進(jìn)入想保存項(xiàng)目的路徑執(zhí)行:
scrapy startproject <your project name>
目錄結(jié)構(gòu)及其說(shuō)明請(qǐng)自行g(shù)oogle或baidu.
在PyCharm中導(dǎo)入:

Spider是整個(gè)項(xiàng)目中最核心的類,在這個(gè)類里我們會(huì)定義抓取對(duì)象(域名、URL)以及抓取規(guī)則。
在spiders下新建python文件:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import scrapy
from scrapy import Selector
from news_xagx.items import NewsXagxItem
class XiaoHuarSpider(scrapy.spiders.Spider):
name = "xasoftpark"
allowed_domains = ["**********.com"]
start_urls = [
"http://www.**********.com/info/iList.jsp?cat_id=10055",
"http://www.**********.com/info/iList.jsp?cat_id=10056",
"http://www.**********.com/info/iList.jsp?cat_id=10057",
"http://www.**********.com/info/iList.jsp?cat_id=10058",
]
def parse(self, response):
baseUrl = 'http://www.**********.com/'
selector = Selector(response)
links = selector.xpath('/html/body/div[8]/div[1]/ul//li/a/@href')
for link in links:
url = baseUrl + link.extract()
yield scrapy.Request(url, callback=self.parse_news_contents)
#爬取下級(jí)頁(yè)面內(nèi)容
def parse_news_contents(self, response):
item = NewsXagxItem()
news_date_content = response.xpath('/html/body/div[5]/div/div[5]').xpath('string(.)').extract()[0]
item['newsDate'] = self.get_news_date(news_date_content)
item['title'] = response.xpath('/html/body/div[5]/div/div[3]').xpath('string(.)').extract()[0]
item['content'] = response.xpath('/html/body/div[5]/div/div[@class="cent_nr_box"]').xpath('string(.)').extract()[0]
yield item
#截取日期
def get_news_date(self, content):
data = u'發(fā)布時(shí)間:'
date_index = content.find(u"發(fā)布時(shí)間:") + len(u"發(fā)布時(shí)間:")
news_data = content[date_index : date_index + 10]
return news_data
MongoDB配置,在settings.py中添加MongoDB配置:
ITEM_PIPELINES = {
'news_xagx.pipelines.NewsXagxPipeline': 300,
}
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "news_xa"
MONGODB_COLLECTION = "news"
pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
import codecs
import json
import pymongo
from scrapy import log
from scrapy.conf import settings
class NewsXagxPipeline:
def __init__(self):
connection = pymongo.MongoClient(settings['MONGODB_SERVER'], settings['MONGODB_PORT'])
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
words_to_filter = ['資金', '政策', 'IT', '創(chuàng)新', '扶持', '項(xiàng)目申報(bào)']
def process_item(self, item, spider):
for word in self.words_to_filter:
if word.decode('utf8') in item['title'][0] or word.decode('utf8') in item['content']:
self.collection.insert(dict(item))
log.msg("news added to MongoDB database!",
level=log.DEBUG, spider=spider)
else:
pass
至此,我們來(lái)驗(yàn)證下是否成功存入MongoDB中:
mongo命令行輸入:
db.news.findOne()
輸出:

至此完成scarpy的入庫(kù),記錄有些簡(jiǎn)陋,慢慢細(xì)化。