摘要:目錄前言創(chuàng)建項(xiàng)目創(chuàng)建創(chuàng)建解析付費(fèi)榜運(yùn)行爬取初始列表調(diào)用腳本獲取詳情前言熟悉之后,本篇文章帶大家爬取七麥數(shù)據(jù)的付費(fèi)應(yīng)用排行榜前名應(yīng)用。根據(jù)傳入的正則表達(dá)式對(duì)數(shù)據(jù)進(jìn)行提取,返回字符串列表。
目錄
前言
創(chuàng)建項(xiàng)目
創(chuàng)建Item
創(chuàng)建Spider
解析付費(fèi)榜
運(yùn)行爬取初始app列表
Selenium調(diào)用JS腳本
獲取app詳情
前言熟悉Scrapy之后,本篇文章帶大家爬取七麥數(shù)據(jù)(https://www.qimai.cn/rank )的ios appstore付費(fèi)應(yīng)用排行榜前100名應(yīng)用。
爬取內(nèi)容包括app在列表中的下標(biāo),app圖標(biāo)地址,app的名稱信息,app的類型,在分類中的排行,開發(fā)者,詳情等。
考慮的問題:
Forbidden by robots.txt的錯(cuò)誤
網(wǎng)頁(yè)返回403
頁(yè)面通過動(dòng)態(tài)渲染,普通的請(qǐng)求url,在頁(yè)面渲染之前已經(jīng)返回response,解析沒有數(shù)據(jù)
列表一頁(yè)20個(gè)app,想要拿到前100個(gè)需要翻頁(yè),但是翻頁(yè)沒有更改url,而是通過js動(dòng)態(tài)加載
...
創(chuàng)建項(xiàng)目在需要放置項(xiàng)目的目錄下,
> scrapy startproject qimairank
回車即可創(chuàng)建默認(rèn)的Scrapy項(xiàng)目架構(gòu)。
創(chuàng)建Item創(chuàng)建Item來(lái)存儲(chǔ)我們爬取的app在列表中的下標(biāo),app圖標(biāo)地址,app的名稱信息,app的類型,在分類中的排行,開發(fā)者,詳情。
修改items.py,在下面增加
class RankItem(scrapy.Item): # 下標(biāo) index = scrapy.Field() # 圖標(biāo)地址 src = scrapy.Field() # app標(biāo)題信息 title = scrapy.Field() # app類型 type = scrapy.Field() # 分類中的排行 type_rank = scrapy.Field() # 開發(fā)者 company = scrapy.Field() # 詳情信息 info = scrapy.Field()創(chuàng)建Spider
在spiders目錄下創(chuàng)建RankSpider.py,并創(chuàng)建class RankSpider,繼承于scrapy.Spider。
import scrapy class RankSpider(scrapy.Spider): name = "RankSpider" start_urls = ["https://www.qimai.cn/rank"] def parse(self, response): pass
name:用于區(qū)別Spider,該名字必須是唯一的。
start_urls:Spider在啟動(dòng)時(shí)進(jìn)行爬取的url列表,首先會(huì)爬取第一個(gè)。
def parse(self, response):得到url的response信息后的解析方法。
解析付費(fèi)榜解析用的Selectors選擇器有多種方法:
xpath(): 傳入xpath表達(dá)式,返回該表達(dá)式所對(duì)應(yīng)的所有節(jié)點(diǎn)的selector list列表 。
css(): 傳入CSS表達(dá)式,返回該表達(dá)式所對(duì)應(yīng)的所有節(jié)點(diǎn)的selector list列表.
extract(): 序列化該節(jié)點(diǎn)為unicode字符串并返回list。
re(): 根據(jù)傳入的正則表達(dá)式對(duì)數(shù)據(jù)進(jìn)行提取,返回unicode字符串list列表。
下面我們用xpath()選擇節(jié)點(diǎn),xpath的語(yǔ)法可參考w3c的http://www.w3school.com.cn/xp... 學(xué)習(xí),需要熟悉語(yǔ)法、運(yùn)算符、函數(shù)等。
def parse(self, response): base = response.xpath( "http://div[@class="ivu-row rank-all-item"]/div[@class="ivu-col ivu-col-span-8"][2]//ul/li[@class="child-item"]/div[@class="ivu-row"]") for box in base: # 創(chuàng)建實(shí)例 rankItem = RankItem() # 下標(biāo) rankItem["index"] = box.xpath(".//div[@class="ivu-col ivu-col-span-3 left-item"]/span/text()").extract()[0] # 圖標(biāo)地址 rankItem["src"] = box.xpath(".//img/@src").extract()[0] # app名稱信息 rankItem["title"] = box.xpath(".//div[@class="info-content"]//a/text()").extract()[0] # app類型 rankItem["type"] = box.xpath(".//div[@class="info-content"]//p[@class="small-txt"]/text()").extract()[0] # 分類中的排行 rankItem["type_rank"] = box.xpath( ".//div[@class="info-content"]//p[@class="small-txt"]//span[@class="rank-item"]/text()").extract()[ 0] # 開發(fā)者 rankItem["company"] = box.xpath( ".//div[@class="info-content"]//p[@class="small-txt"]//span[@class="company-item"]/text()").extract()[ 0] # 詳情頁(yè)地址 infoUrl = "https://www.qimai.cn" + box.xpath(".//div[@class="info-content"]//a/@href").extract()[0] yield rankItem運(yùn)行爬取初始app列表
直接運(yùn)行
qimairank>scrapy crawl RankSpider -o data.json
你會(huì)發(fā)現(xiàn)窗口沒有item輸出,data.json中也沒有數(shù)據(jù),是我們寫錯(cuò)了嗎?
scrapy默認(rèn)遵守robot協(xié)議的,在訪問網(wǎng)址前會(huì)先訪問robot.txt來(lái)查看自己是否有權(quán)限訪問。如果網(wǎng)站不允許被爬,就不能訪問。
怎么樣不遵守協(xié)議呢?
settings.py # Obey robots.txt rules ROBOTSTXT_OBEY = False
再次運(yùn)行仍然失敗,我們來(lái)看下具體原因:
因?yàn)槠啕溇W(wǎng)站對(duì)請(qǐng)求的User-Agent做了校驗(yàn),解決辦法是在配置文件
settings.py # Enable or disable downloader middlewares # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html DOWNLOADER_MIDDLEWARES = { # "qimairank.middlewares.QimairankDownloaderMiddleware": 543, "qimairank.middlewares.RandomUserAgent": 1, } USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ]
并在middlewares.py中創(chuàng)建RandomUserAgent
import random class RandomUserAgent(object): """ 隨機(jī)獲取settings.py中配置的USER_AGENTS設(shè)置"User-Agent" """ def __init__(self, agents): self.agents = agents @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist("USER_AGENTS")) def process_request(self, request, spider): request.headers.setdefault("User-Agent", random.choice(self.agents))
再次運(yùn)行,沒有報(bào)錯(cuò),但是沒有數(shù)據(jù),是我們的xpath寫錯(cuò)啦?我們?cè)趐arse中增加輸出body的信息
可以看到body為空,沒有我們需要的列表數(shù)據(jù),這是因?yàn)槠啕湐?shù)據(jù)是通過js動(dòng)態(tài)渲染的,在渲染完成前,我們的response已經(jīng)返回,那么怎么樣才能等一等呀,等到渲染完成才返回呢?
爬取動(dòng)態(tài)渲染的方式,我知道是通過Splash或者Selenium,像我們的桌面版系統(tǒng)可以選擇用Selenium,操作可以設(shè)置可視化,所有界面操作都能看見,Splash依賴于Docker,無(wú)界面。
安裝Selenium包:
pip install selenium
使用前需要安裝驅(qū)動(dòng),配置詳情點(diǎn)擊
驅(qū)動(dòng)安裝完成,在middlewares.py中創(chuàng)建 SeleniumMiddleware
class SeleniumMiddleware(object): def __init__(self): self.timeout = 50 # 2.Firefox--------------------------------- # 實(shí)例化參數(shù)對(duì)象 options = webdriver.FirefoxOptions() # 無(wú)界面 # options.add_argument("--headless") # 關(guān)閉瀏覽器彈窗 options.set_preference("dom.webnotifications.enabled", False) options.set_preference("dom.push.enabled", False) # 打開瀏覽器 self.browser = webdriver.Firefox(firefox_options=options) # 指定瀏覽器窗口大小 self.browser.set_window_size(1400, 700) # 設(shè)置頁(yè)面加載超時(shí)時(shí)間 self.browser.set_page_load_timeout(self.timeout) self.wait = WebDriverWait(self.browser, self.timeout) def process_request(self, request, spider): # 當(dāng)請(qǐng)求的頁(yè)面不是當(dāng)前頁(yè)面時(shí) if self.browser.current_url != request.url: # 獲取頁(yè)面 self.browser.get(request.url) time.sleep(5) else: pass # 返回頁(yè)面的response return HtmlResponse(url=self.browser.current_url, body=self.browser.page_source, encoding="utf-8", request=request) def spider_closed(self): # 爬蟲結(jié)束 關(guān)閉窗口 self.browser.close() pass @classmethod def from_crawler(cls, crawler): # 設(shè)置爬蟲結(jié)束的回調(diào)監(jiān)聽 s = cls() crawler.signals.connect(s.spider_closed, signal=signals.spider_closed) return s
在settins.py中配置
# Enable or disable downloader middlewares DOWNLOADER_MIDDLEWARES = { # "qimairank.middlewares.QimairankDownloaderMiddleware": 543, "qimairank.middlewares.RandomUserAgent": 1, "qimairank.middlewares.SeleniumMiddleware": 10, }
再次運(yùn)行scrapy crawl RankSpider -o data.json,啦啦啦~這回有數(shù)據(jù)啦。
Selenium調(diào)用JS腳本觀察爬取出來(lái)的data.json,發(fā)現(xiàn)怎么肥四,只有20條數(shù)據(jù),而且除了前6個(gè)的app圖標(biāo)都是七麥的默認(rèn)圖標(biāo)。
這是因?yàn)槠啕湐?shù)據(jù)的列表默認(rèn)每頁(yè)20條,而且默認(rèn)渲染前6個(gè)的圖標(biāo),其余的頁(yè)需要觸發(fā)滑動(dòng)事件加載,而且滑動(dòng)到的圖標(biāo)才開始渲染。這樣怎么辦呢?我們只需要滑動(dòng)到可以加載的按鈕就可以啦,檢查發(fā)現(xiàn)在三個(gè)列表的外層標(biāo)簽有一個(gè)class為cm-explain-bottom的標(biāo)簽
我們用Selenium調(diào)用js腳本,滑動(dòng)到這個(gè)標(biāo)簽就可以啦,在中間件process_request方法更改
def process_request(self, request, spider): # 當(dāng)請(qǐng)求的頁(yè)面不是當(dāng)前頁(yè)面時(shí) if self.browser.current_url != request.url: # 獲取頁(yè)面 self.browser.get(request.url) time.sleep(5) # 請(qǐng)求的url開始為https://www.qimai.cn/rank/時(shí),調(diào)用滑動(dòng)界面,每頁(yè)20個(gè),滑動(dòng)4次 if request.url.startswith("https://www.qimai.cn/rank"): try: for i in (0, 1, 2, 3): self.browser.execute_script( "document.getElementsByClassName("cm-explain-bottom")[0].scrollIntoView(true)") time.sleep(4) except JavascriptException as e: pass except Exception as e: pass
再次執(zhí)行scrapy crawl RankSpider -o data1.json,則可看見已經(jīng)生成data1.json里面有100個(gè)item。
獲取app詳情詳情頁(yè)需要跟進(jìn)url,我們?cè)赗ankSpider#parse方法中,不用yield Item,而是yield Request就可以跟進(jìn)。
# 詳情頁(yè)地址 infoUrl = "https://www.qimai.cn" + box.xpath(".//div[@class="info-content"]//a/@href").extract()[0] # yield rankItem yield Request(infoUrl.replace("rank", "baseinfo"), self.parseInfo, meta={"rankItem": dict(rankItem).copy()}, dont_filter=True)
解析的infoUrl替換"rank"字符串為"baseinfo"就可以訪問app應(yīng)用信息頁(yè),用meta傳遞item到下一個(gè)解析方法中,用軟拷貝的方式,避免Item因?yàn)榈刂废嗤瑑?nèi)容覆蓋。
self.parseInfo為指定這次請(qǐng)求的解析方法,
def parseInfo(self, response): print("基地址:" + response.url) if response.status != 200: return rankItem = response.meta["rankItem"] info = dict() base = response.xpath("http://div[@id="app-container"]") if base.extract(): # try: # 描述 try: info["desc"] = base.xpath( ".//div[@class="app-header"]//div[@class="app-subtitle"]/text()").extract()[0] except Exception as e: print("無(wú)描述") # 開發(fā)商 info["auther"] = base.xpath( ".//div[@class="app-header"]//div[@class="auther"]//div[@class="value"]/text()").extract()[0] # 分類 info["classify"] = base.xpath( ".//div[@class="app-header"]//div[@class="genre"]//div[@class="value"]/a/text()").extract()[0] # appid info["appid"] = base.xpath( ".//div[@class="app-header"]//div[@class="appid"]//div[@class="value"]/a/text()").extract()[0] # appstore地址 info["appstorelink"] = base.xpath( ".//div[@class="app-header"]//div[@class="appid"]//div[@class="value"]/a/@href").extract()[0] # 價(jià)格 info["price"] = base.xpath( ".//div[@class="app-header"]//div[@class="price"]//div[@class="value"]/text()").extract()[0] # 最新版本 info["version"] = base.xpath( ".//div[@class="app-header"]//div[@class="version"]//div[@class="value"]/text()").extract()[0] # 應(yīng)用截圖 info["screenshot"] = base.xpath( ".//div[@class="router-wrapper"]//div[@class="app-screenshot"]//div[@class="screenshot-box"]//img/@src").extract() # 應(yīng)用描述 info["desc"] = base.xpath( ".//div[@class="router-wrapper"]//div[@class="app-describe"]//div[@class="description"]").extract()[ 0] # 應(yīng)用基本信息 info["baseinfo"] = [] for infoBase in base.xpath( ".//div[@class="router-wrapper"]//div[@class="app-baseinfo"]//ul[@class="baseinfo-list"]/li"): # print(info["baseinfo"]) try: info["baseinfo"].append(dict(type=infoBase.xpath(".//*[@class="type"]/text()").extract()[0], info=infoBase.xpath(".//*[@class="info-txt"]/text()").extract()[0])) except Exception as e: pass rankItem["info"] = info # 替換圖標(biāo) 列表加載為默認(rèn)圖標(biāo) rankItem["src"] = response.xpath("http://*[@id="app-side-bar"]//div[@class="logo-wrap"]/img/@src").extract()[ 0] yield rankItem
再次執(zhí)行scrapy crawl RankSpider -o data1.json,則可看見已經(jīng)生成data2.json,但是生成的列表不是排行的列表,甚至是亂序的,原因是因?yàn)槲覀兪褂昧藆rl跟進(jìn)返回,每個(gè)頁(yè)面的請(qǐng)求返回的速度不一樣,需要排序的話就寫個(gè)小腳本按照index排個(gè)序。
項(xiàng)目源碼
原文鏈接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/45201.html
摘要:楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),。本文來(lái)源知乎作者路人甲鏈接楚江數(shù)據(jù)提供網(wǎng)站數(shù)據(jù)采集和爬蟲軟件定制開發(fā)服務(wù),服務(wù)范圍涵蓋社交網(wǎng)絡(luò)電子商務(wù)分類信息學(xué)術(shù)研究等。 楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),http://www.chujiangdata.com。 第一:Python爬蟲學(xué)習(xí)系列教程(來(lái)源于某博主:htt...
摘要:時(shí)間永遠(yuǎn)都過得那么快,一晃從年注冊(cè),到現(xiàn)在已經(jīng)過去了年那些被我藏在收藏夾吃灰的文章,已經(jīng)太多了,是時(shí)候把他們整理一下了。那是因?yàn)槭詹貖A太亂,橡皮擦給設(shè)置私密了,不收拾不好看呀。 ...
摘要:學(xué)習(xí)網(wǎng)絡(luò)爬蟲主要分個(gè)大的版塊抓取,分析,存儲(chǔ)另外,比較常用的爬蟲框架,這里最后也詳細(xì)介紹一下。網(wǎng)絡(luò)爬蟲要做的,簡(jiǎn)單來(lái)說(shuō),就是實(shí)現(xiàn)瀏覽器的功能。 Python學(xué)習(xí)網(wǎng)絡(luò)爬蟲主要分3個(gè)大的版塊:抓取,分析,存儲(chǔ) 另外,比較常用的爬蟲框架Scrapy,這里最后也詳細(xì)介紹一下。 首先列舉一下本人總結(jié)的相關(guān)文章,這些覆蓋了入門網(wǎng)絡(luò)爬蟲需要的基本概念和技巧:寧哥的小站-網(wǎng)絡(luò)爬蟲,當(dāng)我們?cè)跒g覽器中輸入...
摘要:所以如果對(duì)爬蟲有一定基礎(chǔ),上手框架是一種好的選擇。缺少包,使用安裝即可缺少包,使用安裝即可上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)爬取相關(guān)庫(kù)的安裝的安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)爬蟲框架的安裝 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---9、APP爬取相關(guān)庫(kù)的安裝:Appium的安裝下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---11、爬蟲框架的安裝:ScrapySplash、ScrapyRedis 我們直接...
摘要:快速入門首先,初步要做的就是快速構(gòu)建一個(gè)爬蟲。然后把結(jié)果加入到一個(gè)隊(duì)列中。既然是入門,我們肯定是先關(guān)心我們需要的。 因?yàn)楣卷?xiàng)目需求,需要做一個(gè)爬蟲。所以我一個(gè)python小白就被拉去做了爬蟲。花了兩周時(shí)間,拼拼湊湊總算趕出來(lái)了。所以寫個(gè)blog做個(gè)記錄。 快速入門 首先,初步要做的就是快速構(gòu)建一個(gè)爬蟲。 配置環(huán)境 Mac下安裝 1) 直接從官網(wǎng)下載 python下載官網(wǎng) 2) 是通過...
閱讀 2637·2023-04-26 02:17
閱讀 1610·2021-11-24 09:39
閱讀 1070·2021-11-18 13:13
閱讀 2598·2021-09-02 15:11
閱讀 2770·2019-08-30 15:48
閱讀 3406·2019-08-30 14:00
閱讀 2431·2019-08-29 13:43
閱讀 658·2019-08-29 13:07