上一關(guān),我們學(xué)習(xí)了Scrapy框架,知道了Scrapy爬蟲公司的結(jié)構(gòu)和工作原理。
在Scrapy爬蟲公司里,引擎是最大的boss,統(tǒng)領(lǐng)著調(diào)度器、下載器、爬蟲和數(shù)據(jù)管道四大部門。
這四大部門都聽命于引擎,視引擎的需求為最高需求。
我們還通過實(shí)操爬取豆瓣Top250圖書的項(xiàng)目,熟悉了Scrapy的用法。
這一關(guān),我會帶你實(shí)操一個(gè)更大的項(xiàng)目——用Scrapy爬取招聘網(wǎng)站的招聘信息。
你可以借此體驗(yàn)一把當(dāng)Scrapy爬蟲公司CEO的感覺,用代碼控制并操作整個(gè)Scrapy的運(yùn)行。
那我們爬取什么招聘網(wǎng)站呢?在眾多招聘網(wǎng)站中,我挑選了職友集。這個(gè)網(wǎng)站可以通過索引的方式,搜索到全國上百家招聘網(wǎng)站的最新職位。
https://www.jobui.com/rank/company/
我們先對這個(gè)網(wǎng)站做初步的觀察,這樣我們才能明確項(xiàng)目的爬取目標(biāo)。
明確目標(biāo)
打開網(wǎng)址后,你會發(fā)現(xiàn):這是職友集網(wǎng)站的地區(qū)企業(yè)排行榜,里面含有本月人氣企業(yè)榜、最佳口碑雇主、最多粉絲企業(yè)榜和最多評論企業(yè)榜四個(gè)榜單。
點(diǎn)擊【北京字節(jié)跳動(dòng)科技有限公司】,會跳轉(zhuǎn)到這家公司的詳情頁面,再點(diǎn)擊【招聘】,就能看到這家公司正在招聘的所有崗位信息。
初步觀察后,我們可以把爬取目標(biāo)定為:先爬取企業(yè)排行榜四個(gè)榜單里的公司,再接著爬取這些公司的招聘信息。
每個(gè)榜單有10家公司,四個(gè)榜單一共就是40家公司。也就是說,我們要先從企業(yè)排行榜爬取到這40家公司,再跳轉(zhuǎn)到這40家公司的招聘信息頁面,爬取到公司名稱、職位、工作地點(diǎn)和招聘要求。
分析過程
明確完目標(biāo),我們開始分析過程。首先,要看企業(yè)排行榜里的公司信息藏在了哪里。
企業(yè)排行榜的公司信息
請你右擊打開“檢查”工具,點(diǎn)擊Network,刷新頁面。點(diǎn)開第0個(gè)請求company/,看Response,找一下有沒有榜單的公司信息在里面。
一找,發(fā)現(xiàn)四個(gè)榜單的所有公司信息都在里面。說明企業(yè)排行榜的公司信息就藏在html里。
現(xiàn)在請你點(diǎn)擊Elements,點(diǎn)亮光標(biāo),再把鼠標(biāo)移到【北京字節(jié)跳動(dòng)科技有限公司】,這時(shí)就會定位到含有這家公司信息的元素上。
點(diǎn)擊href="/company/10375749/",會跳轉(zhuǎn)到字節(jié)跳動(dòng)這家公司的詳情頁面。詳情頁面的網(wǎng)址是:
https://www.jobui.com/company/10375749/
你再把鼠標(biāo)移到【阿里巴巴集團(tuán)】,點(diǎn)擊href="/company/281097/",會跳轉(zhuǎn)到阿里公司的詳情頁面,頁面的網(wǎng)址為:
https://www.jobui.com/company/281097/
我們可以猜到:/company/+數(shù)字/應(yīng)該是公司id的標(biāo)識。這么一觀察,榜單上的公司詳情頁面的網(wǎng)址規(guī)律我們就得出來了。
那么,我們只要把元素的href屬性的值提取出來,就能構(gòu)造出每家公司詳情頁面的網(wǎng)址。
構(gòu)造公司詳情頁面的網(wǎng)址是為了后面能獲得詳情頁面里的招聘信息。
現(xiàn)在,我們來分析html的結(jié)構(gòu),看看怎樣才能把元素href屬性的值提取出來。
仔細(xì)觀察html的結(jié)構(gòu),你會發(fā)現(xiàn),每個(gè)公司信息都藏在一個(gè)
-
標(biāo)簽。這是一個(gè)層層嵌套的關(guān)系。
我們想拿到所有元素href屬性的值。我們當(dāng)然不能直接用find_all()抓取標(biāo)簽,原因也很簡單:這個(gè)頁面有太多的標(biāo)簽,會抓出來很多我們不想要的信息。
一個(gè)穩(wěn)妥的方案是:先抓取最外層的
-
標(biāo)簽,再抓取
-
標(biāo)簽里的 元素,最后提取到 元素href屬性的值。就像剝洋蔥,要從最外面的一層開始剝一樣。
這里沒有通過抓
分析到這里,我們已經(jīng)知道公司詳情頁面的網(wǎng)址規(guī)律,和如何提取元素href屬性的值。
接下來,我們需要分析的就是,每家公司的詳情頁面。
公司詳情頁面的招聘信息
我們打開【北京字節(jié)跳動(dòng)科技有限公司】的詳情頁面,點(diǎn)擊【招聘】。這時(shí),網(wǎng)址會發(fā)生變化,多了jobs的參數(shù)。
如果你多點(diǎn)擊幾家公司的詳情頁面,查看招聘信息,就會知道:公司招聘信息的網(wǎng)址規(guī)律也是有規(guī)律的。
比如,阿里的招聘信息的網(wǎng)址是:
https://www.jobui.com/company/281097/jobs/
還是在字節(jié)跳動(dòng)公司的招聘信息頁面,右擊打開“檢查”工具,點(diǎn)擊Network,刷新頁面。我們點(diǎn)擊第0個(gè)請求jobs/,查看Response,翻找看看里面有沒有這家公司的招聘信息。
在Response里我們找到了想要的招聘信息。這說明公司的招聘信息依舊是藏在了html里。
接下來,你應(yīng)該知道要分析什么了吧。
分析的套路都是相同的,知道數(shù)據(jù)藏在html后,接著是分析html的結(jié)構(gòu),想辦法提取出我們想要的數(shù)據(jù)。
那就按照慣例點(diǎn)擊Elements,然后點(diǎn)亮光標(biāo),把鼠標(biāo)移到公司名稱吧。
公司名稱藏在
不過經(jīng)過我?guī)状蔚牟僮髟囼?yàn),發(fā)現(xiàn)職友集這個(gè)網(wǎng)站間隔一段時(shí)間就會更換這個(gè)標(biāo)簽的名字(可能你此時(shí)看到的標(biāo)簽名不一定是
)。
為了保證一定能取到公司名稱,我們改成用id屬性(id=“companyH1”)來定位這個(gè)標(biāo)簽。這樣,不管這個(gè)標(biāo)簽名字如何更換,我們依舊能抓到它。
下面,再把鼠標(biāo)移到崗位名稱,看看招聘的崗位信息可以怎么提取。
你會發(fā)現(xiàn):每個(gè)崗位的信息都藏在一個(gè)
div class="job-desc"
的兩個(gè)
s pan
元素里。

這樣分析下來,我們想要的招聘信息,包括公司名稱、職位名稱、工作地點(diǎn)和職位要求,都定位清楚了。
至此,我們分析完了整個(gè)爬取過程,接下來就是代碼實(shí)現(xiàn)啦。
代碼實(shí)現(xiàn)
我們按照Scrapy正常的用法一步步來。首先,我們必須創(chuàng)建一個(gè)Scrapy項(xiàng)目。
創(chuàng)建項(xiàng)目
還記得怎么創(chuàng)建嗎?打開本地電腦的終端(windows:Win+R,輸入cmd;mac:command+空格,搜索“終端”),跳轉(zhuǎn)到你想要保存項(xiàng)目的目錄下,輸入創(chuàng)建Scrapy項(xiàng)目的命令:scrapy startproject jobui(jobui是職友集網(wǎng)站的英文名,在這里我們把它作為Scrapy項(xiàng)目的名字)。
創(chuàng)建好項(xiàng)目后,你在本地電腦的編譯器打開這個(gè)Scrapy項(xiàng)目,會看到如下的結(jié)構(gòu):
定義item
我們剛剛分析的時(shí)候,已經(jīng)確定要爬取的數(shù)據(jù)是公司名稱、職位名稱、工作地點(diǎn)和招聘要求。
那么,現(xiàn)在請你寫出定義item的代碼(提示:要在items.py這個(gè)文件里定義item。
import scrapy
class JobuiItem(scrapy.Item):
#定義了一個(gè)繼承自scrapy.Item的JobuiItem類
company = scrapy.Field()
#定義公司名稱的數(shù)據(jù)屬性
job = scrapy.Field()
#定義職位名稱的數(shù)據(jù)屬性
base = scrapy.Field()
#定義工作地點(diǎn)的數(shù)據(jù)屬性
detail = scrapy.Field()
#定義招聘要求的數(shù)據(jù)屬性
創(chuàng)建和編寫爬蟲文件
定義好item,我們接著要做的是在spiders里創(chuàng)建爬蟲文件,命名為jobui_ jobs。
現(xiàn)在,我們可以開始在這個(gè)爬蟲文件里編寫代碼。
先導(dǎo)入所需的模塊:
import scrapy
import bs4
from ..items import JobuiItem
接下來,是編寫爬蟲的核心代碼。我會先帶著你理清代碼的邏輯,這樣等下你才能比較順利地理解和寫出代碼。
在前面分析過程的步驟里,我們知道要先抓取企業(yè)排行榜40家公司的id標(biāo)識,比如字節(jié)跳動(dòng)公司的id標(biāo)識是/company/10375749/。
再利用抓取到的公司id標(biāo)識構(gòu)造出每家公司招聘信息的網(wǎng)址。比如,字節(jié)跳動(dòng)公司的招聘信息網(wǎng)址就是https://www.jobui.com+/company/10375749/jobs/
我們需要再把每家公司招聘信息的網(wǎng)址封裝成requests對象。這里你可能有點(diǎn)不理解為什么要封裝成requests對象,我解釋一下。
如果我們不是使用Scrapy,而是使用requests庫的話,一般我們得到一個(gè)網(wǎng)址,需要用requests.get(),傳入網(wǎng)址這個(gè)參數(shù),才能獲取到網(wǎng)頁的源代碼。
而在Scrapy里,獲取網(wǎng)頁源代碼這件事兒,會由引擎交分配給下載器去做,不需要我們自己處理。我們之所以要構(gòu)造新的requests對象,是為了告訴引擎,我們新的請求需要傳入什么參數(shù)。
這樣才能讓引擎拿到的是正確requests對象,交給下載器處理。
既然構(gòu)造了新的requests對象,我們就得定義與之匹配的用來處理response的新方法。這樣才能提取出我們想要的招聘信息的數(shù)據(jù)。
好啦,核心代碼的邏輯我們理清楚了。
import scrapy
import bs4
from ..items import JobuiItem
class JobuiSpider(scrapy.Spider):
#定義一個(gè)爬蟲類JobuiSpider
name='jobs'
#定義爬蟲的名字為jobs
allowed_domains=['www.jobui.com']
#定義允許爬蟲爬取網(wǎng)址的域名——職友集網(wǎng)站的域名
start_ulrs=['https://www.jobui.com/rank/company/']
#定義起始網(wǎng)址——職友集企業(yè)排行榜的網(wǎng)址
def parse(self,response):
#parse是默認(rèn)處理response的方法
bs=bs4.BeautifulSoup(response.text,'html.parser')
#用BeautifulSoup解析response(企業(yè)排行榜的網(wǎng)頁源代碼)
ur_list=bs.find_all('ul',class_='textList flsty cfix')
#用find_all提取<'ul' class_='textList flsty cfix'>標(biāo)簽
for ul in ul_list:
#遍歷ul_list
a_list = ul.find_all('a')
#用find_all提取出
第6-13行代碼:定義了爬蟲類JobuiSpider、爬蟲的名字jobs、允許爬蟲爬取的域名和起始網(wǎng)址。
剩下的代碼,你應(yīng)該都能看懂。我們用默認(rèn)的parse方法來處理response(企業(yè)排行榜的網(wǎng)頁源代碼);用BeautifulSoup來解析response;用find_all方法提取數(shù)據(jù)(公司id標(biāo)識)。
公司id標(biāo)識就是元素的href屬性的值,我們想要把它提取出來,就得先抓到所有最外層的
-
標(biāo)簽,再從中抓取所有 元素。
所以這里用了兩個(gè)for循環(huán),把元素的href屬性的值提取了出來,并成功構(gòu)造了公司招聘信息的網(wǎng)址。
代碼寫到這里,我們已經(jīng)完成了核心代碼邏輯的前兩件事:提取企業(yè)排行榜的公司id標(biāo)識和構(gòu)造公司招聘信息的網(wǎng)址。
接下來,就是構(gòu)造新的requests對象和定義新的方法處理response。
繼續(xù)來完善核心代碼(請你重點(diǎn)看第21行代碼及之后的代碼)。
在這里插入代碼片
你應(yīng)該不理解第22行代碼:yield scrapy.Request(real_url, callback=self.parse_job)的意思。我跟你解釋一下。
scrapy.Request是構(gòu)造requests對象的類。real_url是我們往requests對象里傳入的每家公司招聘信息網(wǎng)址的參數(shù)。
callback的中文意思是回調(diào)。self.parse_job是我們新定義的parse_job方法。往requests對象里傳入callback=self.parse_job這個(gè)參數(shù)后,引擎就能知道response要前往的下一站,是parse_job()方法。
yield語句就是用來把這個(gè)構(gòu)造好的requests對象傳遞給引擎。
第26-42行代碼:提取出公司名稱、職位名稱、工作地點(diǎn)和招聘要求這些數(shù)據(jù),并把這些數(shù)據(jù)放進(jìn)我們定義好的JobuiItem類里。
最后,用yield語句把item傳遞給引擎,整個(gè)核心代碼就編寫完啦!ヽ(???)?(???)?
存儲文件
至此,我們整個(gè)項(xiàng)目還差存儲數(shù)據(jù)這一步。在第6關(guān),我們學(xué)過用csv模塊把數(shù)據(jù)存儲csv文件,用openpyxl模塊把數(shù)據(jù)存儲Excel文件。
其實(shí),在Scrapy里,把數(shù)據(jù)存儲成csv文件和Excel文件,也有分別對應(yīng)的方法。我們先說csv文件。
存儲成csv文件的方法比較簡單,只需在settings.py文件里,添加如下的代碼即可。
FEED_URI='./storage/data/%(name)s.csv'
FEED_FORMAT='CSV'
FEED_EXPORT_ENCODING='ansi'
FEED_URI是導(dǎo)出文件的路徑。’./storage/data/%(name)s.csv’,就是把存儲的文件放到與settings.py文件同級的storage文件夾的data子文件夾里。
FEED_FORMAT 是導(dǎo)出數(shù)據(jù)格式,寫CSV就能得到CSV格式。
FEED_EXPORT_ENCODING 是導(dǎo)出文件編碼,ansi是一種在windows上的編碼格式,你也可以把它變成utf-8用在mac電腦上。
存儲成Excel文件的方法要稍微復(fù)雜一些,我們需要先在setting.py里設(shè)置啟用ITEM_PIPELINES,設(shè)置方法如下:
#需要修改`ITEM_PIPELINES`的設(shè)置代碼:
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
# 'jobuitest.pipelines.JobuitestPipeline': 300,
# }
只要取消ITEM_PIPELINES的注釋(刪掉#)即可。
#取消`ITEM_PIPELINES`的注釋后:
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'jobuitest.pipelines.JobuitestPipeline': 300,
}
接著,我們就可以去編輯pipelines.py文件。存儲為Excel文件,我們依舊是用openpyxl模塊來實(shí)現(xiàn),代碼如下,注意閱讀注釋:
import openpyxl
class JobuiPipeline(object):
#定義一個(gè)JobuiPipeline類,負(fù)責(zé)處理item
def __init__(self):
#初始化函數(shù) 當(dāng)類實(shí)例化時(shí)這個(gè)方法會自啟動(dòng)
self.wb =openpyxl.Workbook()
#創(chuàng)建工作薄
self.ws = self.wb.active
#定位活動(dòng)表
self.ws.append(['公司', '職位', '地址', '招聘信息'])
#用append函數(shù)往表格添加表頭
def process_item(self, item, spider):
#process_item是默認(rèn)的處理item的方法,就像parse是默認(rèn)處理response的方法
line = [item['company'], item['position'], item['address'], item['detail']]
#把公司名稱、職位名稱、工作地點(diǎn)和招聘要求都寫成列表的形式,賦值給line
self.ws.append(line)
#用append函數(shù)把公司名稱、職位名稱、工作地點(diǎn)和招聘要求的數(shù)據(jù)都添加進(jìn)表格
return item
#將item丟回給引擎,如果后面還有這個(gè)item需要經(jīng)過的itempipeline,引擎會自己調(diào)度
def close_spider(self, spider):
#close_spider是當(dāng)爬蟲結(jié)束運(yùn)行時(shí),這個(gè)方法就會執(zhí)行
self.wb.save('./jobui.xlsx')
#保存文件
self.wb.close()
#關(guān)閉文件
修改設(shè)置
在最后,我們還要再修改Scrapy中settings.py文件里的默認(rèn)設(shè)置:添加請求頭,以及把
ROBOTSTXT_OBEY=True
改成
ROBOTSTXT_OBEY=False
。
#需要修改的默認(rèn)設(shè)置:
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'douban (+http://www.yourdomain.com)'
# Obey robots.txt rules
ROBOTSTXT_OBEY = True
還有一處默認(rèn)設(shè)置我們需要修改,代碼如下:
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 0
我們需要取消DOWNLOAD_DELAY = 0這行的注釋(刪掉#)。DOWNLOAD_DELAY翻譯成中文是下載延遲的意思,這行代碼可以控制爬蟲的速度。因?yàn)檫@個(gè)項(xiàng)目的爬取速度不宜過快,我們要把下載延遲的時(shí)間改成0.5秒。
改好后的代碼如下:
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 0.5
修改完設(shè)置,我們已經(jīng)可以運(yùn)行代碼。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
