在上一節(jié)里面哗总,我定義了結(jié)構(gòu)化字段Item真屯,然而并沒有用到它脸候。
所以,為了能夠?qū)⒂杏玫男畔⒄淼絀tem中去绑蔫,我們需要了解一下提取頁面有效信息的辦法运沦。
- 這里要用到一個(gè)小工具——Selectors(選擇器)。
Selector是Scrapy內(nèi)置的功能配深,它支持使用XPath和CSS Selector兩種表達(dá)式來對(duì)信息進(jìn)行搜索携添。
如果有同學(xué)用過正則表達(dá)式的話,對(duì)以上兩種語言應(yīng)該有些感性認(rèn)識(shí)了篓叶。不過比起正則烈掠,也許BeautifulSoup插件中的find Tag與Selector更加相似羞秤,它們都可以很方便地對(duì)提取出html中的標(biāo)簽。(其實(shí)寫這段我有點(diǎn)不確定的左敌,因?yàn)閾?jù)說XPath應(yīng)該是一門在 XML 文檔中查找信息的語言瘾蛋,XML和HTML之間的差異,我理解得不夠深刻矫限。)
這里附上XPath的教程:http://www.w3school.com.cn/xpath/index.asp
Step1:網(wǎng)頁源代碼分析
現(xiàn)在哺哼,我們就打開上一節(jié)中我們保存下來的tencent.txt,我從中選取了一小段我們比較感興趣的數(shù)據(jù)叼风。
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=42477&keywords=&tid=0&lid=0">SNG16-騰訊音樂多媒體AI研究員(深圳)</a></td>
<td>技術(shù)類</td>
<td>3</td>
<td>深圳</td>
<td>2018-07-14</td>
</tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=42471&keywords=&tid=0&lid=0">22989-專有云網(wǎng)絡(luò)運(yùn)維工程師(北京/上海/深圳)</a><span class="hot"> </span></td>
<td>技術(shù)類</td>
<td>2</td>
<td>深圳</td>
<td>2018-07-14</td>
</tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=42472&keywords=&tid=0&lid=0">22989-專有云數(shù)據(jù)庫運(yùn)維工程師(北京/上海/深圳)</a><span class="hot"> </span></td>
<td>技術(shù)類</td>
<td>2</td>
<td>深圳</td>
<td>2018-07-14</td>
</tr>
初看雜亂的信息中也是有不少規(guī)律的嘛取董。
看第一行中的odd
和下面幾行中的even
,再結(jié)合剛才看到的原網(wǎng)頁无宿,可以猜出這分別代表了表格中的奇數(shù)行和偶數(shù)行茵汰,他們的背景色不一樣的。
經(jīng)過瀏覽器的渲染孽鸡,摘錄的這段呈現(xiàn)在我們眼中蹂午,就是表格的最后三行了。
現(xiàn)在回頭看一下上節(jié)筆記中定義的Item信息梭灿,就可以一一對(duì)應(yīng)上了画侣,以最后一條為例:
- name = 22989-專有云數(shù)據(jù)庫運(yùn)維工程師(北京/上海/深圳)
- detailLink = position_detail.php?id=42472&keywords=&tid=0&lid=0
- catalog = 技術(shù)類
- recruitNumber = 2
- workLocation = 深圳
- publishTime = 2018-07-14
固然我們可以手動(dòng)從中挑出想要的信息來,但最終的目的依然是讓程序幫我們完成一切堡妒,我們對(duì)html源代碼的關(guān)注配乱,是為了找出有用信息的特征。
- 可以看出一對(duì)
<tr>
包括了一條招聘信息皮迟,而<td>
括起了一條信息中的各個(gè)元素搬泥。
那是不是如果按順序提取出(tr[1],tr[2],……)再索引到(td[1]伏尼,td[2]忿檩,……)就可以對(duì)應(yīng)上我們想要的信息了呢?
沒錯(cuò)爆阶,不過在XPath里有更簡(jiǎn)單形式的語法能夠幫我們找到他們燥透。
Step2:XPath語法
表達(dá)式 | 描述 |
---|---|
nodename | 選取nodename節(jié)點(diǎn)的所有子節(jié)點(diǎn)。 |
. | 選取當(dāng)前節(jié)點(diǎn)辨图。 |
.. | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)班套。 |
@ | 選取屬性。 |
[ ] | 根據(jù)中括號(hào)的內(nèi)容作篩選 |
* | 通配符:選擇所有元素節(jié)點(diǎn) |
還有一個(gè)表達(dá)式符號(hào)我想單獨(dú)拿出來說故河,就是/
吱韭,大家知道/
在系統(tǒng)里用作文件夾層次的分隔,同樣地鱼的,/
應(yīng)該配合以上表達(dá)式食用理盆。單獨(dú)的/
表示根節(jié)點(diǎn)位置痘煤。
除了表達(dá)式,XPath還有一類構(gòu)成要素:運(yùn)算符猿规。運(yùn)算符可以連接表達(dá)式衷快,以更方便地完成任務(wù)。我列出了感覺會(huì)常用的坎拐,想了解更多可以查看這個(gè)鏈接:http://www.w3school.com.cn/xpath/xpath_operators.asp
運(yùn)算符 | 描述 | 實(shí)例 | 返回值 |
---|---|---|---|
| | 計(jì)算兩個(gè)節(jié)點(diǎn)集 | //book | //cd | 返回所有擁有 book 和 cd 元素的節(jié)點(diǎn)集 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90烦磁,則返回 true养匈。如果 price 是 9.70哼勇,則返回 false。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80呕乎,則返回 true积担。如果 price 是 9.50,則返回 false猬仁。 |
and | 與 | price>9.00 and price<9.90 | 如果 price 是 9.80帝璧,則返回 true。如果 price 是 8.50湿刽,則返回 false的烁。 |
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=42477&keywords=&tid=0&lid=0">SNG16-騰訊音樂多媒體AI研究員(深圳)</a></td>
<td>技術(shù)類</td>
<td>3</td>
<td>深圳</td>
<td>2018-07-14</td>
</tr>
表格中信息的特征很好辨認(rèn),奇數(shù)行的內(nèi)容一定具有屬性 "odd"诈闺,我們可以編寫表達(dá)式把奇數(shù)行的找出來:
'//*[@class="odd"]'
- 表達(dá)式的解釋
剛才提到/
可以表示根結(jié)點(diǎn)渴庆,那么//
又是什么意思呢,它表示從當(dāng)前節(jié)點(diǎn)開始遞歸下降雅镊,此路徑運(yùn)算符出現(xiàn)在模式開頭時(shí)襟雷,表示應(yīng)從根節(jié)點(diǎn)遞歸下降。
所以代碼的意思就是:從根節(jié)點(diǎn)開始尋找所有符合要求的節(jié)點(diǎn)仁烹,要求是該行屬性為奇數(shù)行耸弄。
可是一般來說我們關(guān)注的對(duì)象可不僅僅是奇數(shù)行,還有偶數(shù)行呢卓缰。所以啊用上面提到的節(jié)點(diǎn)集符號(hào)|
修改一下表達(dá)式就可以了:
//*[@class="even"] | //*[@class="odd"]
這樣计呈,我們就找到了一條招聘信息的節(jié)點(diǎn),為了能夠分別保存職位名稱征唬、類型捌显、工作地點(diǎn)等信息,我們需要對(duì)上一步找到的節(jié)點(diǎn)做進(jìn)一步處理:
'./td[1]/a/text()'#當(dāng)前節(jié)點(diǎn)中鳍鸵,第1個(gè)td節(jié)點(diǎn)里苇瓣,a節(jié)點(diǎn)內(nèi)的文字:name
'./td[1]/a/@href'#當(dāng)前節(jié)點(diǎn)中,第1個(gè)td節(jié)點(diǎn)里偿乖,a節(jié)點(diǎn)內(nèi)击罪,herf屬性的內(nèi)容:detailLink
'./td[2]/text()'#當(dāng)前節(jié)點(diǎn)中哲嘲,第2個(gè)td節(jié)點(diǎn)里,a節(jié)點(diǎn)內(nèi)的文字:catalog
'./td[3]/text()'#當(dāng)前節(jié)點(diǎn)中媳禁,第3個(gè)td節(jié)點(diǎn)里眠副,a節(jié)點(diǎn)內(nèi)的文字:recruitNumber
'./td[4]/text()'#當(dāng)前節(jié)點(diǎn)中,第4個(gè)td節(jié)點(diǎn)里竣稽,a節(jié)點(diǎn)內(nèi)的文字:workLocation
'./td[5]/text()'#當(dāng)前節(jié)點(diǎn)中囱怕,第5個(gè)td節(jié)點(diǎn)里,a節(jié)點(diǎn)內(nèi)的文字:publishTime
一一對(duì)應(yīng)的關(guān)系找到了毫别,恭喜我們娃弓,終于理解XPath的初級(jí)用法了。接下來要把命令交給爬蟲執(zhí)行岛宦,就需要把它放在tencent.py的parse函數(shù)中蔓涧。
Step3:編寫爬取代碼
打開tencent.py眉孩,在開頭添加上我們對(duì)items.py里定義好的結(jié)構(gòu)化字段的引用苹熏,然后修改整個(gè)文檔的代碼如下:
import scrapy
from tutorial.items import RecruitItem
class RecruitSpider(scrapy.Spider):
name = "tencent"
allowed_domains = ["hr.tencent.com"]
start_urls = [
"http://hr.tencent.com/position.php?&start=0#a"
]
def parse(self, response):
for sel in response.xpath('//*[@class="even"] | //*[@class="odd"]'):
name = sel.xpath('./td[1]/a/text()').extract()[0]
detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
catalog = sel.xpath('./td[2]/text()').extract()[0]
recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
workLocation = sel.xpath('./td[4]/text()').extract()[0]
publishTime = sel.xpath('./td[5]/text()').extract()[0]
item = RecruitItem()
item['name'] = name.encode('utf-8')
item['detailLink'] = detailLink.encode('utf-8')
item['catalog'] = catalog.encode('utf-8')
item['recruitNumber'] = recruitNumber.encode('utf-8')
item['workLocation'] = workLocation.encode('utf-8')
item['publishTime'] = publishTime.encode('utf-8')
yield item
代碼執(zhí)行部分結(jié)構(gòu)有3層:
-
for sel in response.xpath('//*[@class="even"] | //*[@class="odd"]'):
首先找出頁面中所有符合表格奇行和偶行特征的節(jié)點(diǎn)翩蘸,然后用sel這個(gè)臨時(shí)變量去遍歷他們; -
name = sel.xpath('./td[1]/a/text()').extract()[0]
用XPath表達(dá)式去尋找sel中符合要求的元素变汪,分別存入臨時(shí)的字段里侠坎。
至于為什么語法是這樣的,可以參考:
xpath().extract()和xpath().extract()[0] 的區(qū)別裙盾? - 知乎用戶的回答 - 知乎
https://www.zhihu.com/question/63370553/answer/247633004 -
item['name'] = name.encode('utf-8')
將臨時(shí)字段里的內(nèi)容轉(zhuǎn)碼成UTF-8再存入item的各字段中实胸。yield類似于C語言的return。
至此代碼部分就編寫完成啦~
Step4:爬取信息
來來闷煤,再度回到\tutorial文件夾下童芹,運(yùn)行終端,輸入:
scrapy crawl tencent -o items.json
即在當(dāng)前目錄下生成了items.json鲤拿。
JSON語言能夠方便地被讀取假褪,我理解為輕量的數(shù)據(jù)庫。
我是第一次處理json格式的語言近顷,所以在用NPP記事本打開文檔的時(shí)候生音,望著格式一團(tuán)糟的json頭暈了。不過為了降低學(xué)習(xí)成本窒升,暫時(shí)還沒有開數(shù)據(jù)庫的坑缀遍。那就找一個(gè)能夠方便地把UTF-8代碼顯示成漢字的工具吧。
我是選擇了NPP的插件JSON Viewer饱须,可以對(duì)JSON可視化排版域醇,也能把UTF-8翻譯成漢字,大概是這樣的:
嗯……就是這樣。
但是這個(gè)界面……求一款顏值高的本地工具譬挚,謝謝锅铅。