爬蟲框架Scrapy實戰之批量抓取招聘資訊--附原始碼
瞭解更多Python爬蟲內容請微信公眾號關注:Python技術博文
所謂網路爬蟲,就是一個在網上到處或定向抓取資料的程式,當然,這種說法不夠專業,更專業的描述就是,抓取特定網站網頁的HTML資料。不過由於一個網站的網頁很多,而我們又不可能事先知道所有網頁的URL地址,所以,如何保證我們抓取到了網站的所有HTML頁面就是一個有待考究的問題了。一般的方法是,定義一個入口頁面,然後一般一個頁面會有其他頁面的URL,於是從當前頁面獲取到這些URL加入到爬蟲的抓取佇列中,然後進入到新頁面後再遞迴的進行上述的操作,其實說來就跟深度遍歷或廣度遍歷一樣。
Scrapy是一個基於Twisted,純Python實現的爬蟲框架,使用者只需要定製開發幾個模組就可以輕鬆的實現一個爬蟲,用來抓取網頁內容以及各種圖片,非常之方便~
Scrapy 使用 Twisted這個非同步網路庫來處理網路通訊,架構清晰,並且包含了各種中介軟體介面,可以靈活的完成各種需求。整體架構如下圖所示:
綠線是資料流向,首先從初始URL 開始,Scheduler 會將其交給 Downloader 進行下載,下載之後會交給 Spider 進行分析,Spider分析出來的結果有兩種:一種是需要進一步抓取的連結,例如之前分析的“下一頁”的連結,這些東西會被傳回 Scheduler ;另一種是需要儲存的資料,它們則被送到Item Pipeline 那裡,那是對資料進行後期處理(詳細分析、過濾、儲存等)的地方。另外,在資料流動的通道里還可以安裝各種中介軟體,進行必要的處理。
我假定你已經安裝了Scrapy。假如你沒有安裝,你可以參考這篇文章。
在本文中,我們將學會如何使用Scrapy建立一個爬蟲程式,並爬取指定網站上的內容
1. 建立一個新的Scrapy Project
2. 定義你需要從網頁中提取的元素Item
3.實現一個Spider類,通過介面完成爬取URL和提取Item的功能
4. 實現一個Item PipeLine類,完成Item的儲存功能
我將會用騰訊招聘官網作為例子。
Github原始碼:https://github.com/maxliaops/scrapy-itzhaopin
目標:抓取騰訊招聘官網職位招聘資訊並儲存為JSON格式。
新建工程
首先,為我們的爬蟲新建一個工程,首先進入一個目錄(任意一個我們用來儲存程式碼的目錄),執行:
scrapy startprojectitzhaopin
最後的itzhaopin就是專案名稱。這個命令會在當前目錄下建立一個新目錄itzhaopin,結構如下:
.
├── itzhaopin
│ ├── itzhaopin
│ │ ├── __init__.py
│ │ ├── items.py
│ │ ├── pipelines.py
│ │ ├── settings.py
│ │ └── spiders
│ │ └── __init__.py
│ └── scrapy.cfg
scrapy.cfg: 專案配置檔案
items.py: 需要提取的資料結構定義檔案
pipelines.py:管道定義,用來對items裡面提取的資料做進一步處理,如儲存等
settings.py: 爬蟲配置檔案
spiders: 放置spider的目錄
定義Item
在items.py裡面定義我們要抓取的資料:
1 2 3 4 5 6 7 8 |
from scrapy.item import Item, Field
class TencentItem(Item):
name = Field() # 職位名稱
catalog = Field() # 職位類別
workLocation = Field() # 工作地點
recruitNumber = Field() # 招聘人數
detailLink = Field() # 職位詳情頁連結
publishTime = Field() # 釋出時間
|
實現Spider
Spider是一個繼承自scrapy.contrib.spiders.CrawlSpider的Python類,有三個必需的定義的成員
name: 名字,這個spider的標識
start_urls:一個url列表,spider從這些網頁開始抓取
parse():一個方法,當start_urls裡面的網頁抓取下來之後需要呼叫這個方法解析網頁內容,同時需要返回下一個需要抓取的網頁,或者返回items列表
所以在spiders目錄下新建一個spider,tencent_spider.py:
import re
import json
from scrapy.selector import Selector
try :
from scrapy.spider import Spider
except :
from scrapy.spider import BaseSpider as Spider
from scrapy.utils.response import get_base_url
from scrapy.utils.url import urljoin_rfc
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor as sle
from itzhaopin.items import *
from itzhaopin.misc.log import *
class TencentSpider(CrawlSpider):
name = "tencent"
allowed_domains = [ "tencent.com" ]
start_urls = [
"http://hr.tencent.com/position.php"
]
rules = [ # 定義爬取URL的規則
Rule(sle(allow = ( "/position.php\?&start=\d{,4}#a" )), follow = True , callback = 'parse_item' )
]
items = []
sel = Selector(response)
base_url = get_base_url(response)
sites_even = sel.css( 'table.tablelist tr.even' )
for site in sites_even:
item = TencentItem()
item[ 'name' ] = site.css( '.l.square a' ).xpath( 'text()' ).extract()
relative_url = site.css( '.l.square a' ).xpath( '@href' ).extract()[ 0 ]
item[ 'detailLink' ] = urljoin_rfc(base_url, relative_url)
item[ 'catalog' ] = site.css( 'tr > td:nth-child(2)::text' ).extract()
item[ 'workLocation' ] = site.css( 'tr > td:nth-child(4)::text' ).extract()
item[ 'recruitNumber' ] = site.css( 'tr > td:nth-child(3)::text' ).extract()
item[ 'publishTime' ] = site.css( 'tr > td:nth-child(5)::text' ).extract()
items.append(item)
#print repr(item).decode("unicode-escape") + '\n'
sites_odd = sel.css( 'table.tablelist tr.odd' )
for site in sites_odd:
item = TencentItem()
item[ 'name' ] = site.css( '.l.square a' ).xpath( 'text()' ).extract()
relative_url = site.css( '.l.square a' ).xpath( '@href' ).extract()[ 0 ]
item[ 'detailLink' ] = urljoin_rfc(base_url, relative_url)
item[ 'catalog' ] = site.css( 'tr > td:nth-child(2)::text' ).extract()
item[ 'workLocation' ] = site.css( 'tr > td:nth-child(4)::text' ).extract()
item[ 'recruitNumber' ] = site.css( 'tr > td:nth-child(3)::text' ).extract()
item[ 'publishTime' ] = site.css( 'tr > td:nth-child(5)::text' ).extract()
items.append(item)
#print repr(item).decode("unicode-escape") + '\n'
info( 'parsed ' + str (response))
return items
def _process_request( self , request):
info( 'process ' + str (request))
return request
|
實現PipeLine
PipeLine用來對Spider返回的Item列表進行儲存操作,可以寫入到檔案、或者資料庫等。
PipeLine只有一個需要實現的方法:process_item,例如我們將Item儲存到JSON格式檔案中:
pipelines.py
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from scrapy import signals
import json
import codecs
class JsonWithEncodingTencentPipeline( object ):
def __init__( self ):
self . file = codecs. open ( 'tencent.json' , 'w' , encoding = 'utf-8' )
def process_item( self , item, spider):
line = json.dumps( dict (item), ensure_ascii = False ) + "\n"
self . file .write(line)
return item
def spider_closed( self , spider):
self . file .close(
)
|
到現在,我們就完成了一個基本的爬蟲的實現,可以輸入下面的命令來啟動這個Spider:
1 |
scrapy crawl tencent
|
爬蟲執行結束後,在當前目錄下將會生成一個名為tencent.json的檔案,其中以JSON格式儲存了職位招聘資訊。
部分內容如下:
{"recruitNumber": ["1"], "name": ["SD5-資深手遊策劃(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15626&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["產品/專案類"], "workLocation": ["深圳"]}
{"recruitNumber": ["1"], "name": ["TEG13-後臺開發工程師(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15666&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技術類"], "workLocation": ["深圳"]}
{"recruitNumber": ["2"], "name": ["TEG12-資料中心高階經理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15698&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技術類"], "workLocation": ["深圳"]}
{"recruitNumber": ["1"], "name": ["GY1-微信支付品牌策劃經理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15710&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["市場類"], "workLocation": ["深圳"]}
{"recruitNumber": ["2"], "name": ["SNG06-後臺開發工程師(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15499&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技術類"], "workLocation": ["深圳"]}
{"recruitNumber": ["2"], "name": ["OMG01-騰訊時尚視訊策劃編輯(北京)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15694&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["內容編輯類"], "workLocation": ["北京"]}
{"recruitNumber": ["1"], "name": ["HY08-QT客戶端Windows開發工