數(shù)據(jù)是企業(yè)能夠擁有的最有價(jià)值的資產(chǎn)之一。它是數(shù)據(jù)科學(xué)和數(shù)據(jù)分析的核心:沒有數(shù)據(jù),它們都是過時(shí)的。積極收集數(shù)據(jù)的企業(yè)可能比不收集數(shù)據(jù)的公司具有競(jìng)爭(zhēng)優(yōu)勢(shì)。有了足夠的數(shù)據(jù),組織可以更好地確定問題的原因并做出明智的決定。
在某些情況下,組織可能缺乏足夠的數(shù)據(jù)來得出必要的見解。例如,初創(chuàng)企業(yè)幾乎總是在沒有數(shù)據(jù)的情況下開始。與其抱怨他們的不足,更好的解決方案是使用數(shù)據(jù)采集技術(shù)來幫助構(gòu)建定制數(shù)據(jù)庫。
這篇文章介紹了一種流行的數(shù)據(jù)采集技術(shù),稱為網(wǎng)絡(luò)抓取。您可以使用 kurtispykes/web-scraping-real-estate-data GitHub 存儲(chǔ)庫中的代碼進(jìn)行后續(xù)操作。
什么是數(shù)據(jù)采集?
Data acquisition (也稱為 DAQ )可能與技術(shù)人員記錄烤箱溫度一樣簡(jiǎn)單。您可以將 DAQ 定義為對(duì)測(cè)量真實(shí)世界物理現(xiàn)象的信號(hào)進(jìn)行采樣,并將生成的樣本轉(zhuǎn)換為計(jì)算機(jī)可以解釋的數(shù)字?jǐn)?shù)值的過程。
在一個(gè)理想的世界里,我們將把所有的數(shù)據(jù)都交給我們,隨時(shí)可以使用。然而,世界遠(yuǎn)非理想。數(shù)據(jù)采集技術(shù)之所以存在,是因?yàn)槟承﹩栴}需要特定的數(shù)據(jù),而您在特定時(shí)間可能無法訪問這些數(shù)據(jù)。在進(jìn)行任何數(shù)據(jù)分析之前,數(shù)據(jù)團(tuán)隊(duì)必須有足夠的可用數(shù)據(jù)。一種獲取數(shù)據(jù)的技術(shù)是 web scraping 。
什么是網(wǎng)頁刮取?
Web 抓取是一種流行的數(shù)據(jù)采集技術(shù),在那些對(duì)大數(shù)據(jù)需求不斷增長的人中,它已成為討論的熱門話題。從本質(zhì)上講,這是一個(gè)從互聯(lián)網(wǎng)中提取信息并將其格式化以便于在數(shù)據(jù)分析和數(shù)據(jù)科學(xué)管道中使用的過程。
在過去,網(wǎng)頁抓取是一個(gè)手動(dòng)過程。這個(gè)過程既乏味又耗時(shí),而且人類容易出錯(cuò)。最常見的解決方案是自動(dòng)化。 web 刮取的自動(dòng)化使您能夠加快流程,同時(shí)節(jié)省資金并減少人為錯(cuò)誤的可能性。
然而,網(wǎng)絡(luò)抓取也有其挑戰(zhàn)。
刮網(wǎng)的挑戰(zhàn)
除了知道如何編程和理解 HTML 之外,構(gòu)建自己的 web scraper 還有很多挑戰(zhàn)。提前了解數(shù)據(jù)收集過程中可能遇到的各種障礙是有益的。以下是從 web 上抓取數(shù)據(jù)時(shí)面臨的一些最常見的挑戰(zhàn)。
robots.txt
抓取數(shù)據(jù)的權(quán)限通常保存在 robots.txt 文件中。此文件用于通知爬網(wǎng)程序可在網(wǎng)站上訪問的 URL 。它可以防止站點(diǎn)被請(qǐng)求過載。
在開始網(wǎng)頁抓取項(xiàng)目之前,首先要檢查的是目標(biāo)網(wǎng)站是否允許網(wǎng)頁抓取。網(wǎng)站可以決定是否允許在其網(wǎng)站上使用刮片器進(jìn)行刮片。
有些網(wǎng)站不允許自動(dòng)抓取網(wǎng)頁,這通常是為了防止競(jìng)爭(zhēng)對(duì)手獲得競(jìng)爭(zhēng)優(yōu)勢(shì),并從目標(biāo)站點(diǎn)中耗盡服務(wù)器資源。它確實(shí)會(huì)影響網(wǎng)站的性能。
您可以通過將 /robots.txt 附加到域名來檢查網(wǎng)站的 robots.txt 文件。例如,檢查 Twitter 的 robots.txt 如下: www.twitter.com/robots.txt 。
結(jié)構(gòu)變化
UI 和 UX 開發(fā)人員定期對(duì)網(wǎng)站進(jìn)行添加、刪除和定期結(jié)構(gòu)更改,以使其跟上最新進(jìn)展。 Web scraper 依賴于構(gòu)建 scraper 時(shí)網(wǎng)頁的代碼元素。因此,頻繁更改網(wǎng)站可能會(huì)導(dǎo)致數(shù)據(jù)丟失。隨時(shí)關(guān)注網(wǎng)頁的更改總是一個(gè)好主意。
此外,考慮到不同的網(wǎng)頁設(shè)計(jì)者在設(shè)計(jì)網(wǎng)頁時(shí)可能有不同的標(biāo)準(zhǔn)。這意味著,如果你計(jì)劃抓取多個(gè)網(wǎng)站,你可能需要構(gòu)建多個(gè)抓取器,每個(gè)網(wǎng)站一個(gè)。
IP 攔截器,或被禁止
它有可能被禁止進(jìn)入網(wǎng)站。如果你的網(wǎng)絡(luò)爬蟲向某個(gè)網(wǎng)站發(fā)送的請(qǐng)求數(shù)量非常高,那么你的 IP 地址可能會(huì)被禁止。或者,網(wǎng)站可能會(huì)限制其訪問,以打破刮取過程。
在被認(rèn)為是道德的和不道德的網(wǎng)絡(luò)抓取之間有一條細(xì)線:越過這條線很快就會(huì)導(dǎo)致 IP 屏蔽。
驗(yàn)證碼
CAPTCHA (完全自動(dòng)化的公共圖靈測(cè)試,以區(qū)分計(jì)算機(jī)和人類)正是它的名字所說的:區(qū)分人類和機(jī)器人。驗(yàn)證碼所帶來的問題對(duì)于人類來說通常是合乎邏輯的、簡(jiǎn)單明了的,但對(duì)于機(jī)器人來說,要完成同樣的任務(wù)是很有挑戰(zhàn)性的,這可以防止網(wǎng)站被垃圾郵件攻擊。
使用 CAPTCHA 抓取網(wǎng)站有一些合乎道德的解決方法。然而,這一討論超出了本文的范圍。
蜜罐陷阱
類似于你如何設(shè)置設(shè)備或外殼來捕捉入侵你家的害蟲,網(wǎng)站所有者設(shè)置蜜罐陷阱來捕捉刮器。這些陷阱通常是人類看不到的鏈接,但對(duì)網(wǎng)絡(luò)爬蟲來說是可見的。
其目的是獲取有關(guān)刮板的信息,例如其 IP 地址,以便他們阻止刮板訪問網(wǎng)站。
實(shí)時(shí)數(shù)據(jù)抓取
在某些情況下,您可能需要實(shí)時(shí)丟棄數(shù)據(jù)(例如,價(jià)格比較)。由于更改隨時(shí)可能發(fā)生,因此刮取器必須不斷監(jiān)控網(wǎng)站并刮取數(shù)據(jù)。實(shí)時(shí)獲取大量數(shù)據(jù)具有挑戰(zhàn)性。
Web 抓取最佳實(shí)踐
現(xiàn)在,您已經(jīng)意識(shí)到可能面臨的挑戰(zhàn),了解最佳實(shí)踐以確保您在道德上收集網(wǎng)絡(luò)數(shù)據(jù)非常重要。
尊重 robots.txt
在抓取 web 數(shù)據(jù)時(shí),您將面臨的一個(gè)挑戰(zhàn)是遵守 robots.txt 的條款。遵循網(wǎng)站設(shè)置的關(guān)于 web 抓取可以抓取和不能抓取的指南是最佳做法。
如果一個(gè)網(wǎng)站不允許網(wǎng)絡(luò)抓取,那么抓取該網(wǎng)站是不道德的。最好找到另一個(gè)數(shù)據(jù)源,或者直接聯(lián)系網(wǎng)站所有者,討論解決方案。
善待服務(wù)器
Web 服務(wù)器只能承受這么多。超過 web 服務(wù)器的負(fù)載會(huì)導(dǎo)致服務(wù)器崩潰。考慮向主機(jī)服務(wù)器發(fā)出請(qǐng)求的可接受頻率。短時(shí)間內(nèi)的幾個(gè)請(qǐng)求可能會(huì)導(dǎo)致服務(wù)器故障,進(jìn)而破壞網(wǎng)站其他訪問者的用戶體驗(yàn)。
保持請(qǐng)求之間的合理時(shí)間間隔,并考慮并行請(qǐng)求的數(shù)量。
非高峰時(shí)段刮水
了解一個(gè)網(wǎng)站什么時(shí)候可能會(huì)收到最多的流量,并避免在這段時(shí)間內(nèi)進(jìn)行抓取。您的目標(biāo)是不妨礙其他訪問者的用戶體驗(yàn)。當(dāng)一個(gè)網(wǎng)站收到的流量減少時(shí),你的道德責(zé)任是刮一刮。(這也對(duì)您有利,因?yàn)樗@著提高了刮板的速度。)
剪貼畫教程
Python 中的 Web 抓取通常涉及從零開始編寫幾個(gè)低級(jí)任務(wù)。然而, Scrapy ,一個(gè)開源的網(wǎng)絡(luò)爬行框架,默認(rèn)情況下處理幾個(gè)常見的啟動(dòng)需求。這意味著您可以專注于從目標(biāo)網(wǎng)站中提取所需的數(shù)據(jù)。
為了展示 Scrapy 的威力,您開發(fā)了一個(gè) spider ,這是一個(gè) Scrapy 類,您可以在其中定義刮網(wǎng)器的行為。使用這個(gè)蜘蛛從 Boston Realty Advisors 網(wǎng)站上抓取所有列表。
在開始任何 web 刮取項(xiàng)目之前,檢查目標(biāo)網(wǎng)站以進(jìn)行刮取非常重要。在檢查網(wǎng)站時(shí),我喜歡檢查的第一件事是頁面是靜態(tài)的還是由 JavaScript 生成的。
要打開開發(fā)人員工具箱,請(qǐng)按 F12 。在 Network 選項(xiàng)卡上,確保選中 Disable cache 。
要打開命令選項(xiàng)板,請(qǐng)按 CTRL + SHIFT + P ( Windows Linux )或 command + SHIFT-P ( Mac )。鍵入 Disable JavaScript ,然后按 Enter 鍵并重新加載目標(biāo)網(wǎng)站。
空頁面告訴您目標(biāo)網(wǎng)頁是使用 JavaScript 生成的。您無法通過解析開發(fā)者工具箱 Elements 選項(xiàng)卡中顯示的 HTML 元素來抓取頁面。在開發(fā)人員工具中重新啟用 JavaScript 并重新加載頁面。
還有更多要檢查的。
I 在開發(fā)人員工具箱中,選擇 XHR ( XMLHTTPRequest )選項(xiàng)卡。瀏覽發(fā)送到服務(wù)器的請(qǐng)求后,我注意到一些有趣的事情。有兩個(gè)請(qǐng)求稱為“ inventory ”,但在一個(gè)請(qǐng)求中,您可以訪問 JSON 中第一頁上的所有數(shù)據(jù)。
這一信息非常有用,因?yàn)檫@意味著您不必訪問波士頓房地產(chǎn)顧問網(wǎng)站上的主列表頁面即可獲取所需的數(shù)據(jù)。
現(xiàn)在你可以開始刮了。(我做了更多的檢查,以更好地理解如何模擬向服務(wù)器發(fā)出的請(qǐng)求,但這超出了本文的范圍。)
創(chuàng)建報(bào)廢項(xiàng)目
要設(shè)置 Scrapy 項(xiàng)目,首先安裝scrapy。我建議在 virtual environment 中執(zhí)行此步驟。
pip install scrapy
激活虛擬環(huán)境后,輸入以下命令:
scrapy startproject bradvisors
該命令創(chuàng)建一個(gè)名為bradvisors的 Scrapy 項(xiàng)目。 Scrapy 還會(huì)自動(dòng)將一些文件添加到目錄中。
運(yùn)行命令后,最終目錄結(jié)構(gòu)如下所示:
. └── bradvisors ├── bradvisors │ ├── __init__.py │ ├── items.py │ ├── middlewares.py │ ├── pipelines.py │ ├── settings.py │ └── spiders │ └── __init__.py └── scrapy.cfg
到目前為止,您已經(jīng)檢查了網(wǎng)站的元素,并創(chuàng)建了 Scrapy 項(xiàng)目。
構(gòu)建蜘蛛
蜘蛛模塊必須構(gòu)建在bradvisors/bradvisors/spiders目錄中。我的蜘蛛腳本的名稱是bradvisors_spider.py,但您可以使用自定義名稱。
以下代碼從該網(wǎng)站中提取數(shù)據(jù)。代碼示例僅在items.py文件更新時(shí)成功運(yùn)行。有關(guān)詳細(xì)信息,請(qǐng)參閱示例后的說明。
import json import scrapy from bradvisors.items import BradvisorsItem class BradvisorsSpider(scrapy.Spider): name = "bradvisors" start_urls = ["https://bradvisors.com/listings/"] url = "https://buildout.com/plugins/5339d012fdb9c122b1ab2f0ed59a55ac0327fd5f/inventory" headers = { 'authority': 'buildout.com', 'accept': 'application/json, text/javascript, */*; q=0.01', 'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8', 'cache-control': 'no-cache', 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 'origin': 'https://buildout.com', 'pragma': 'no-cache', 'referer': 'https://buildout.com/plugins/5339d012fdb9c122b1ab2f0ed59a55ac0327fd5f/bradvisors.com/inventory/?pluginId=0&iframe=true&embedded=true&cacheSearch=true&=undefined', 'sec-ch-ua': '"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', 'x-newrelic-id': 'Vg4GU1RRGwIJUVJUAwY=', 'x-requested-with': 'XMLHttpRequest' } def parse(self, response): url = "https://buildout.com/plugins/5339d012fdb9c122b1ab2f0ed59a55ac0327fd5f/inventory" # There are 5 pages on the website for i in range(5): # Change the page number in the payload payload = f"utf8=%E2%9C%93&polygon_geojson=&lat_min=&lat_max=&lng_min=&lng_max=&mobile_lat_min= &mobile_lat_max=&mobile_lng_min=&mobile_lng_max=&page={str(i)}&map_display_limit=500&map_type=roadmap &custom_map_marker_url=%2F%2Fs3.amazonaws.com%2Fbuildout-production%2Fbrandings%2F7242%2Fprofile_photo %2Fsmall.png%3F1607371909&use_marker_clusterer=true&placesAutoComplete=&q%5Btype_use_offset_eq_any%5D%5B%5D= &q%5Bsale_or_lease_eq%5D=&q%5Bbuilding_size_sf_gteq%5D=&q%5Bbuilding_size_sf_lteq%5D=&q%5B listings_data_max_space_available_on_market_gteq%5D=&q%5Blistings_data_min_space_available_on_market_lteq %5D=&q%5Bproperty_research_property_year_built_gteq%5D=&q%5Bproperty_research_property_year_built_lteq %5D=&q%5Bproperty_use_id_eq_any%5D%5B%5D=&q%5Bcompany_office_id_eq_any%5D%5B%5D=&q%5Bs%5D%5B%5D=" # Crawl the data, given the payload yield scrapy.Request(method="POST", body=payload, url=url, headers=self.headers, callback=self.parse_api) def parse_api(self, response): # Response is json, use loads to convert it into Python dictionary data = json.loads(response.body) # Our item object defined in items.py item = BradvisorsItem() for listing in data["inventory"]: item["address"] = listing["address_one_line"] item["city"] = listing["city"] item["city_state"] = listing["city_state"] item["zip"] = listing["zip"] item["description"] = listing["description"] item["size_summary"] = listing["size_summary"] item["item_url"] = listing["show_link"] item["property_sub_type_name"] = listing["property_sub_type_name"] item["sale"] = listing["sale"] item["sublease"] = listing["sublease"] yield item
該代碼完成以下任務(wù):
將刮刀的名稱定義為bradvisors。
定義隨請(qǐng)求傳遞的標(biāo)頭。
指定parse方法是刮板運(yùn)行時(shí)的自動(dòng)回調(diào)。
定義parse方法,在該方法中,您遍歷要抓取的頁數(shù),將頁碼傳遞給有效負(fù)載,并生成該請(qǐng)求。這會(huì)在每次迭代時(shí)調(diào)用parse_api方法。
定義parse_api方法并將有效的 JSON 響應(yīng)轉(zhuǎn)換為 Python 字典。
在items.py中定義BradvisorsItem類(下一個(gè)代碼示例)。
循環(huán)遍歷庫存中的所有列表并抓取特定元素。
# items.py import scrapy class BradvisorsItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() address = scrapy.Field() city = scrapy.Field() city_state = scrapy.Field() zip = scrapy.Field() description = scrapy.Field() size_summary = scrapy.Field() item_url = scrapy.Field() property_sub_type_name = scrapy.Field() sale = scrapy.Field()
接下來,必須執(zhí)行刮取以解析數(shù)據(jù)。
運(yùn)行鏟運(yùn)機(jī)
從命令行導(dǎo)航到項(xiàng)目的根目錄(在本例中為bradvisors)。運(yùn)行以下命令:
scrapy crawl bradvisors -o data.csv
該命令將抓取 Boston Realty Advisors 網(wǎng)站,并將提取的數(shù)據(jù)保存在項(xiàng)目根目錄的data.csv文件中。
太好了,您現(xiàn)在已經(jīng)獲得了房地產(chǎn)數(shù)據(jù)!
接下來是什么?
隨著對(duì)大數(shù)據(jù)需求的增長,讓自己具備使用網(wǎng)絡(luò)抓取工具獲取數(shù)據(jù)的能力是一項(xiàng)非常有價(jià)值的技能。從互聯(lián)網(wǎng)上刪除數(shù)據(jù)可能會(huì)帶來一些挑戰(zhàn)。除了獲取所需的數(shù)據(jù)外,您的目標(biāo)還應(yīng)該是尊重網(wǎng)站,并以道德的方式收集網(wǎng)站。
-
NVIDIA
+關(guān)注
關(guān)注
14文章
4990瀏覽量
103104 -
AI
+關(guān)注
關(guān)注
87文章
30946瀏覽量
269187
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論