实战案例 | Scrapy 集成Selenium爬取智联招聘数据
初學scrapy之后,發現就是效率對比于selenium和requests快了很多,那么問題來了,如果網站設置了反爬,比如User-Agent反爬,cookie反爬,IP封禁等等,所以我們需要通過集成selenium到scrapy中,繞過網站反爬,達到目的。
這里選擇智聯招聘網站作為案例,就是雖然不是動態網頁,但是它需要模擬登錄,所以我們通過scrapy集成selenium進行數據抓取。
一、需求分析
打開目標網站,搜索web前端開發工程師。
這是首頁,由于我的當前位置在武漢,所以系統自動定位到武漢,點擊搜索后:
這個就是需要通過selenium出路的一個點。
手動登錄后得到以下界面:
我們的目標是每一條招聘信息的8條數據:
name 職位名稱
salary 薪資
adress ?地區
experience 經驗
eduBack ?教育背景
company 公司名稱
companyType 公司類型
scale 公司規模
info 簡介
二、scrapy項目文件配置
定義items
import?scrapyclass?ZlzpItem(scrapy.Item):name?=?scrapy.Field()***薪資??公司??規模...***info?=?scrapy.Field()定義scrapy爬蟲:zl.py(智聯)
#這里先說明下url: firstPageUrl?:?'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p=1'#作為第一頁的url,下面的myspider.py中就不在展示,避免代碼冗余。base_url?=?'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'然后下面是zl.py的源碼:(分為幾個部分)
1、初始化設置:
#?-*-?coding:?utf-8?-*- import?scrapy from?zlzp.items?import?ZlzpItemcount?=?1???#?定義一個全局變量,與base_url構建?下一頁的url? class?ZlSpider(scrapy.Spider):name?=?'zl'allowed_domains?=?['zhaopin.com']start_urls?=?[firstPageUrl]2、parse函數:
????def?parse(self,?response):global?countcount?+=?1??#?每解析一次頁面,讓count+1,和baseurl構造下一頁的urljobList?=?response.xpath('//div[@class="positionlist"]/div/a')for?job?in?jobList:name?=??job.xpath("./div[1]/div[1]/span[1]/text()").extract_first()?...salary***,company***,....info?=?job.xpath("./div[3]/div[1]//text()").extract_first()item?=?ZlzpItem(name=name,salary=salary,company=company,adress=adress,experience=experience,eduBack=eduBack,companyType=companyType,scale=scale,info=info)yield?item3、分頁:
????????next_url?=?'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'.format(count)if?count?==?34:return?None?#?設置程序停止的條件if?next_url:yield?scrapy.Request(next_url,callback=self.parse)定義下載器中間件(DownloadMiddleware):myDownloadMiddleware.py
from?selenium.webdriver.support.ui?import?WebDriverWait from?selenium.webdriver.support?import?expected_conditions?as?EC ... class?ZlzpDownloaderMiddleware:def?__init__(self):self.driver?=?webdriver.Chrome()def?process_request(self,?request,?spider):self.driver.get(request.url)time.sleep(3)?#?休息3s#?設置顯示等待,由于需要登錄,我們手機掃碼登錄,知道頁面出現(即url顯示為firstpageurl)WebDriverWait(self.driver,?1000).until(EC.url_contains(request.url))time.sleep(6)?#?登錄成功之后頁面需要時間加載出來,再休息幾秒return?HtmlResponse(url=self.driver.current_url,?body=self.driver.page_source,?encoding="utf-8",request=request)??#?然后把這個response對象返回給爬蟲(zl.py)說明:
selenium集成到scrapy中的核心就是在爬蟲中間件中攔截請求,把處理后的響應對象返回,對應于爬蟲文件(這里的zl.py)parse函數中的response,如果不集成selenium,那么response對象不能很好應對網站的反爬.
此處的parse_request方法中只有少量的selenium代碼,因為動態操作其實不多.
重點:return后面的response對象:
在這里我們不能return None,如果return None,那么請求會被發送到下載中間件去下載這個頁面,在將這個頁面的response返回給spider(hr.py)。但是我們上面browser.get的時候就已經下載了這個頁面的內容,所以沒有必要在下載一次,我們只要制定一個response對象,直接返回這個response給spider即可
定義管道(Pipeline):pipelines.py
from?itemadapter?import?ItemAdapter import?csvclass?ZlzpPipeline:def?__init__(self):self.f?=?open('zlJob.csv',?'w',?encoding='utf-8',?newline='')#?self.file_name?=?['name','upTime','salary','needs','welfare','company','scale','types']self.file_name?=?['name','salary','company','adress','experience','eduBack','companyType','scale','info']?self.writer?=?csv.DictWriter(self.f,?fieldnames=self.file_name)self.writer.writeheader()def?process_item(self,?item,?spider):self.writer.writerow(dict(item))#?寫入spider傳過來的具體數值return?item?#?寫入完返回def?close_spider(self,?spider):self.f.close()settings.py配置
BOT_NAME?=?'zlzp'SPIDER_MODULES?=?['zlzp.spiders'] NEWSPIDER_MODULE?=?'zlzp.spiders' LOG_LEVEL?=?'WARNING' ...... ROBOTSTXT_OBEY?=?False...... DEFAULT_REQUEST_HEADERS?=?{'user-agent':?'Mozilla/5.0?(Windows?NT?10.0;?Win64;?x64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/91.0.4472.106?Safari/537.36','Accept':?'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':?'en', } ...... DOWNLOADER_MIDDLEWARES?=?{'zlzp.middlewares.ZlzpDownloaderMiddleware':?543, } ...... ITEM_PIPELINES?=?{'zlzp.pipelines.ZlzpPipeline':?300, } ............表示注釋代碼,這里省略。
三、程序運行
命令行鍵入:
scrapy?crawl?hrpic1:運行程序結束到第34頁,對應count = 34
pic02:(csv文件)
四、數據簡單分析
查看數據
import?pandas?as?pd df?=?pd.read_csv('./zlJob.csv') df.head()薪資餅圖展示
c?=?(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",?[list(z)?for?z?in?zip(typesX,number)])???#?zip函數兩個部分組合在一起list(zip(x,y))----->?[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="類型:"))??#?標題.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:?{c}"))??#?數據標簽設置 )c.render_notebook()??經驗要求柱圖展示
from?pyecharts.charts?import?Bar bar?=?Bar() bar.add_xaxis(['3-5年',?'1-3年',?'不限',?'5-10年',?'無經驗',?'1年以下',?'10年以上']) bar.add_yaxis('經驗要求',[462,329,83,78,19,15,4]) bar.render()學歷要求柱圖展示
c?=?(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",?[list(z)?for?z?in?zip(educationTypes,number)])???#?zip函數兩個部分組合在一起list(zip(x,y))----->?[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="類型:"))??#?標題.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:?{c}"))??#?數據標簽設置 )c.render_notebook()??大多數要求本科學歷,或者說大專及以上學歷。
公司類型柱圖展示
c?=?(Pie(init_opts=opts.InitOpts(bg_color="white")).add("",?[list(z)?for?z?in?zip(companyTypes,number)])???#?zip函數兩個部分組合在一起list(zip(x,y))----->?[(x,y)].set_global_opts(title_opts=opts.TitleOpts(title="類型:"))??#?標題.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:?{c}"))??#?數據標簽設置 )c.render_notebook()??可以看到大多數公司是民營或者上市公司。
五、總結
頁面翻頁處理,由于我們只是使用selenium就是打開網頁請求數據,所以一般在爬蟲文件中進行翻頁處理,如果對應的下一頁的a標簽的href屬性不是下一頁的頁面url,我們需要設置動態全局變量,構建動態的url。
下載中間件中設置的selenium的相關操作,動態點擊,頁面滾輪操作,顯隱式等待等等,重要的是返回的response對象,這個是集成selenimu到scrapy的核心,在下載中間件中攔截請求,把處理后的response對象返回給爬蟲。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?分享給更多朋友,轉發,點贊,在看
總結
以上是生活随笔為你收集整理的实战案例 | Scrapy 集成Selenium爬取智联招聘数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CVPR2019:Domain-Spec
- 下一篇: 回忆詹姆斯·高斯林的Java时代