Python爬取鏈家成都二手房源資訊 asyncio + aiohttp 非同步爬蟲實戰
葉庭雲
本文先熟悉併發與並行、阻塞與非阻塞、同步與非同步、多執行緒、多執行緒、協程的基本概念。再實現asyncio + aiohttp爬取鏈家成都二手房源資訊的非同步爬蟲,爬取效率與多執行緒版進行簡單測試和比較。
1. 基本概念
併發與並行
併發: 指在同一時刻只能有一條指令執行,但多個程式指令被快速的輪換執行,使得在宏觀上具有多個程式同時執行的效果,但在微觀上並不是同時執行的,只是把時間分成若干段,使多個程式快速交替的執行。
並行: 指在同一時刻,有多條指令在多個處理器上同時執行。所以無論從微觀還是從宏觀來看,二者都是一起執行的。
阻塞與非阻塞
阻塞狀態指程式未得到所需計算資源時被掛起的狀態。程式在等待某個操作完成期間,自身無法繼續處理其他的事情,則稱該程式在該操作上是阻塞的。
非阻塞:程式在等待某操作過程中,自身不被阻塞,可以繼續處理其他的事情,則稱該程式在該操作上是非阻塞的。
同步與非同步
同步:不同程式單元為了完成某個任務,在執行過程中需靠某種通訊方式以協調一致,我們稱這些程式單元是同步執行的。
非同步:為完成某個任務,不同程式單元之間過程中無需通訊協調,也能完成任務的方式,不相關的程式單元之間可以是非同步的。
多執行緒
多執行緒(multithreading),是指從軟體或者硬體上實現多個執行緒併發執行的技術。具有多執行緒能力的計算機因有硬體支援而能夠在同一時間執行多於一個執行緒,進而提升整體處理效能。具有這種能力的系統包括對稱多處理機、多核心處理器以及晶片級多處理或同時多執行緒處理器。在一個程式中,這些獨立執行的程式片段叫作“執行緒”(Thread),利用它程式設計的概念就叫作“多執行緒處理”。
多程式
多程式(multiprocessing),每個正在系統上執行的程式都是一個程式。每個程式包含一到多個執行緒。程式也可能是整個程式或者是部分程式的動態執行。執行緒是一組指令的集合,或者是程式的特殊段,它可以在程式裡獨立執行,也可以把它理解為程式碼執行的上下文。所以執行緒基本上是輕量級的程式,它負責在單個程式裡執行多工。多程式就是利用 CPU 的多核優勢,在同一時間並行地執行多個任務,可以大大提高執行效率。
協程
協程,英文叫作 Coroutine,又稱微執行緒、纖程,協程是一種使用者態的輕量級執行緒。
協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此協程能保留上一次呼叫時的狀態,即所有區域性狀態的一個特定組合,每次過程重入時,就相當於進入上一次呼叫的狀態。協程本質上是個單程式,協程相對於多程式來說,無需執行緒上下文切換的開銷,無需原子操作鎖定及同步的開銷,程式設計模型也非常簡單。我們可以使用協程來實現非同步操作,比如在網路爬蟲場景下,我們發出一個請求之後,需要等待一定的時間才能得到響應,但其實在這個等待過程中,程式可以幹許多其他的事情,等到響應得到之後才切換回來繼續處理,這樣可以充分利用 CPU 和其他資源,這就是協程的優勢。
2. asyncio + aiohttp 非同步爬蟲
爬蟲基本思路:
確定目標url
傳送請求 獲取響應
解析響應 提取資料
儲存資料
檢視網頁原始碼,可以找到我們想要提取的資料
檢查分析網頁:
可以發現一頁裡的每條房源的各種資訊都在li標籤下
第1頁:
第2頁:pg2/
第3頁:pg3/
第100頁:pg100/
分析易得翻頁的規律,構造請求url列表。
非同步爬蟲程式碼如下:
import asyncioimport aiohttpfrom lxml import etreeimport loggingimport datetimeimport openpyxl
wb = openpyxl.Workbook()sheet = wb.active
sheet.append(['房源', '房子資訊', '所在區域', '單價', '關注人數和釋出時間', '標籤'])logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')start = datetime.datetime.now()class Spider(object):
def __init__(self):
self.semaphore = asyncio.Semaphore(6) # 訊號量,控制協程數,防止爬的過快被反爬
self.header = {
"Host": "cd.lianjia.com",
"Referer": "",
"Cookie": "lianjia_uuid=db0b1b8b-01df-4ed1-b623-b03a9eb26794; _smt_uid=5f2eabe8.5e338ce0; UM_distinctid=173ce4f874a51-0191f33cd88e85-b7a1334-144000-173ce4f874bd6; _jzqy=1.1596894185.1596894185.1.jzqsr=baidu.-; _ga=GA1.2.7916096.1596894188; gr_user_id=6aa4d13e-c334-4a71-a611-d227d96e064a; Hm_lvt_678d9c31c57be1c528ad7f62e5123d56=1596894464; _jzqx=1.1596897192.1596897192.1.jzqsr=cd%2Elianjia%2Ecom|jzqct=/ershoufang/pg2/.-; select_city=510100; lianjia_ssid=c9a3d829-9d20-424d-ac4f-edf23ae82029; Hm_lvt_9152f8221cb6243a53c83b956842be8a=1596894222,1597055584; gr_session_id_a1a50f141657a94e=33e39c24-2a1c-4931-bea2-90c3cc70389f; CNZZDATA1253492306=874845162-1596890927-https%253A%252F%252F%252F%7C1597054876; CNZZDATA1254525948=1417014870-1596893762-https%253A%252F%252F%252F%7C1597050413; CNZZDATA1255633284=1403675093-1596890300-https%253A%252F%252F%252F%7C1597052407; CNZZDATA1255604082=1057147188-1596890168-https%253A%252F%252F%252F%7C1597052309; _qzjc=1; gr_session_id_a1a50f141657a94e_33e39c24-2a1c-4931-bea2-90c3cc70389f=true; _jzqa=1.3828792903266388500.1596894185.1596897192.1597055585.3; _jzqc=1; _jzqckmp=1; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22173ce4f8b4f317-079892aca8aaa8-b7a1334-1327104-173ce4f8b50247%22%2C%22%24device_id%22%3A%22173ce4f8b4f317-079892aca8aaa8-b7a1334-1327104-173ce4f8b50247%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; _gid=GA1.2.865060575.1597055587; Hm_lpvt_9152f8221cb6243a53c83b956842be8a=1597055649; srcid=eyJ0Ijoie1wiZGF0YVwiOlwiOWQ4ODYyNmZhMmExM2Q0ZmUxMjk1NWE2YTRjY2JmODZiZmFjYTc2N2U1ZTc2YzM2ZDVkNmM2OGJlOWY5ZDZhOWNkN2U3YjlhZWZmZTllNGE3ZTUwYjA3NGYwNDEzMThkODg4NTBlMWZhZmRjNTIwNDBlMDQ2Mjk2NTYxOWQ1Y2VlZjE5N2FhZjUyMTZkOTcyZjg4YzNiM2U1MThmNjc5NmQ4MGUxMmU2YTM4MmI3ZmU0NmFhNTJmYmMyYWU1ZWI3MjU5YWExYTQ1YWFkZDUyZWVjMzM2NTFjYTA2M2NlM2ExMzZhNjEwYjFjYzQ0OTY5MTQwOTA4ZjQ0MjQ3N2ExMDkxNTVjODFhN2MzMzg5YWM3MzBmMTQxMjU4NzAwYzk5ODE3MTk1ZTNiMjc4NWEzN2M3MTIwMjdkYWUyODczZWJcIixcImtleV9pZFwiOlwiMVwiLFwic2lnblwiOlwiYmExZDJhNWZcIn0iLCJyIjoiaHR0cHM6Ly9jZC5saWFuamlhLmNvbS9lcnNob3VmYW5nLyIsIm9zIjoid2ViIiwidiI6IjAuMSJ9; _qzja=1.726562344.1596894309336.1596897192124.1597055583833.1597055601626.1597055649949.0.0.0.12.3; _qzjb=1.1597055583833.3.0.0.0; _qzjto=3.1.0; _jzqb=1.3.10.1597055585.1; _gat=1; _gat_past=1; _gat_global=1; _gat_new_global=1; _gat_dianpu_agent=1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
}
async def scrape(self, url):
async with self.semaphore:
session = aiohttp.ClientSession(headers=self.header)
response = await session.get(url)
result = await response.text()
await session.close()
return result async def scrape_index(self, page):
url = f'pg{page}/'
text = await self.scrape(url)
await self.parse(text)
async def parse(self, text):
html = etree.HTML(text)
lis = html.xpath('//*[@id="content"]/div[1]/ul/li')
for li in lis:
house_data = li.xpath('.//div[@class="title"]/a/text()')[0] # 房源
house_info = li.xpath('.//div[@class="houseInfo"]/text()')[0] # 房子資訊
address = ' '.join(li.xpath('.//div[@class="positionInfo"]/a/text()')) # 位置資訊
price = li.xpath('.//div[@class="priceInfo"]/div[2]/span/text()')[0] # 單價 元/平米
attention_num = li.xpath('.//div[@class="followInfo"]/text()')[0] # 關注人數和釋出時間
tag = ' '.join(li.xpath('.//div[@class="tag"]/span/text()')) # 標籤
sheet.append([house_data, house_info, address, price, attention_num, tag])
logging.info([house_data, house_info, address, price, attention_num, tag])
def main(self):
# 100頁的資料
scrape_index_tasks = [asyncio.ensure_future(self.scrape_index(page)) for page in range(1, 101)]
loop = asyncio.get_event_loop()
tasks = asyncio.gather(*scrape_index_tasks)
loop.run_until_complete(tasks)if __name__ == '__main__':
spider = Spider()
spider.main()
wb.save('house.xlsx')
delta = (datetime.datetime.now() - start).total_seconds()
print("用時:{:.3f}s".format(delta))12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
執行結果如下:
成功爬取了100頁的資料,共有3000條房源資訊,用時15.976s。
多執行緒版爬蟲如下:
import requestsfrom lxml import etreeimport openpyxlfrom concurrent.futures import ThreadPoolExecutorimport datetimeimport logging
headers = {
"Host": "cd.lianjia.com",
"Referer": "",
"Cookie": "lianjia_uuid=db0b1b8b-01df-4ed1-b623-b03a9eb26794; _smt_uid=5f2eabe8.5e338ce0; UM_distinctid=173ce4f874a51-0191f33cd88e85-b7a1334-144000-173ce4f874bd6; _jzqy=1.1596894185.1596894185.1.jzqsr=baidu.-; _ga=GA1.2.7916096.1596894188; gr_user_id=6aa4d13e-c334-4a71-a611-d227d96e064a; Hm_lvt_678d9c31c57be1c528ad7f62e5123d56=1596894464; _jzqx=1.1596897192.1596897192.1.jzqsr=cd%2Elianjia%2Ecom|jzqct=/ershoufang/pg2/.-; select_city=510100; lianjia_ssid=c9a3d829-9d20-424d-ac4f-edf23ae82029; Hm_lvt_9152f8221cb6243a53c83b956842be8a=1596894222,1597055584; gr_session_id_a1a50f141657a94e=33e39c24-2a1c-4931-bea2-90c3cc70389f; CNZZDATA1253492306=874845162-1596890927-https%253A%252F%252F%252F%7C1597054876; CNZZDATA1254525948=1417014870-1596893762-https%253A%252F%252F%252F%7C1597050413; CNZZDATA1255633284=1403675093-1596890300-https%253A%252F%252F%252F%7C1597052407; CNZZDATA1255604082=1057147188-1596890168-https%253A%252F%252F%252F%7C1597052309; _qzjc=1; gr_session_id_a1a50f141657a94e_33e39c24-2a1c-4931-bea2-90c3cc70389f=true; _jzqa=1.3828792903266388500.1596894185.1596897192.1597055585.3; _jzqc=1; _jzqckmp=1; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22173ce4f8b4f317-079892aca8aaa8-b7a1334-1327104-173ce4f8b50247%22%2C%22%24device_id%22%3A%22173ce4f8b4f317-079892aca8aaa8-b7a1334-1327104-173ce4f8b50247%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; _gid=GA1.2.865060575.1597055587; Hm_lpvt_9152f8221cb6243a53c83b956842be8a=1597055649; srcid=eyJ0Ijoie1wiZGF0YVwiOlwiOWQ4ODYyNmZhMmExM2Q0ZmUxMjk1NWE2YTRjY2JmODZiZmFjYTc2N2U1ZTc2YzM2ZDVkNmM2OGJlOWY5ZDZhOWNkN2U3YjlhZWZmZTllNGE3ZTUwYjA3NGYwNDEzMThkODg4NTBlMWZhZmRjNTIwNDBlMDQ2Mjk2NTYxOWQ1Y2VlZjE5N2FhZjUyMTZkOTcyZjg4YzNiM2U1MThmNjc5NmQ4MGUxMmU2YTM4MmI3ZmU0NmFhNTJmYmMyYWU1ZWI3MjU5YWExYTQ1YWFkZDUyZWVjMzM2NTFjYTA2M2NlM2ExMzZhNjEwYjFjYzQ0OTY5MTQwOTA4ZjQ0MjQ3N2ExMDkxNTVjODFhN2MzMzg5YWM3MzBmMTQxMjU4NzAwYzk5ODE3MTk1ZTNiMjc4NWEzN2M3MTIwMjdkYWUyODczZWJcIixcImtleV9pZFwiOlwiMVwiLFwic2lnblwiOlwiYmExZDJhNWZcIn0iLCJyIjoiaHR0cHM6Ly9jZC5saWFuamlhLmNvbS9lcnNob3VmYW5nLyIsIm9zIjoid2ViIiwidiI6IjAuMSJ9; _qzja=1.726562344.1596894309336.1596897192124.1597055583833.1597055601626.1597055649949.0.0.0.12.3; _qzjb=1.1597055583833.3.0.0.0; _qzjto=3.1.0; _jzqb=1.3.10.1597055585.1; _gat=1; _gat_past=1; _gat_global=1; _gat_new_global=1; _gat_dianpu_agent=1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
}logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')wb = openpyxl.Workbook()sheet = wb.active
sheet.append(['房源', '房子資訊', '所在區域', '單價', '關注人數和釋出時間', '標籤'])start = datetime.datetime.now()def get_house(page):
if page == 1:
url = ""
else:
url = f"pg{page}/"
res = requests.get(url, headers=headers)
html = etree.HTML(res.text)
lis = html.xpath('//*[@id="content"]/div[1]/ul/li')
for li in lis:
house_data = li.xpath('.//div[@class="title"]/a/text()')[0] # 房源
house_info = li.xpath('.//div[@class="houseInfo"]/text()')[0] # 房子資訊
address = ' '.join(li.xpath('.//div[@class="positionInfo"]/a/text()')) # 位置資訊
price = li.xpath('.//div[@class="priceInfo"]/div[2]/span/text()')[0] # 單價 元/平米
attention_num = li.xpath('.//div[@class="followInfo"]/text()')[0] # 關注人數和釋出時間
tag = ' '.join(li.xpath('.//div[@class="tag"]/span/text()')) # 標籤
sheet.append([house_data, house_info, address, price, attention_num, tag])
logging.info([house_data, house_info, address, price, attention_num, tag])if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=6) as executor:
executor.map(get_house, [page for page in range(1, 101)])
wb.save('house.xlsx')
delta = (datetime.datetime.now() - start).total_seconds()
print("用時:{:.3f}s".format(delta))12345678910111213141516171819202122232425262728293031323334353637383940414243
執行結果如下:
成功爬取了100頁的資料,共有3000條房源資訊,用時16.796s。
3. 其他說明
從以上簡單測試可以看出,將非同步請求靈活運用在爬蟲中,在伺服器能承受高併發的前提下增加併發數量,爬取效率提升是非常可觀的。
爬蟲程式碼僅用於python爬蟲知識交流,勿作其他用途,違者後果自負。
不建議抓取太多資料,容易對伺服器造成負載,淺嘗輒止即可。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2723140/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python爬蟲--爬取鏈家租房資訊Python爬蟲
- 基於asyncio、aiohttp、xpath的非同步爬蟲AIHTTP非同步爬蟲
- Python非同步爬蟲(aiohttp版)Python非同步爬蟲AIHTTP
- Python爬蟲實戰:爬取淘寶的商品資訊Python爬蟲
- 初識Scrapy框架+爬蟲實戰(7)-爬取鏈家網100頁租房資訊框架爬蟲
- Python asyncio 爬蟲Python爬蟲
- 爬蟲 | 非同步請求aiohttp模組爬蟲非同步AIHTTP
- 大規模非同步新聞爬蟲: 用asyncio實現非同步爬蟲非同步爬蟲
- 爬蟲實戰(一):爬取微博使用者資訊爬蟲
- python爬蟲——爬取大學排名資訊Python爬蟲
- 用python爬取鏈家的租房資訊Python
- Python爬蟲實戰詳解:爬取圖片之家Python爬蟲
- Python3.X 爬蟲實戰(併發爬取)Python爬蟲
- Python爬蟲爬取淘寶,京東商品資訊Python爬蟲
- 小白學 Python 爬蟲(25):爬取股票資訊Python爬蟲
- 爬蟲實戰(二):Selenium 模擬登入並爬取資訊爬蟲
- Python 爬蟲實戰Python爬蟲
- Python爬蟲實戰案例-爬取幣世界標紅快訊Python爬蟲
- 【Python爬蟲實戰】使用Selenium爬取QQ音樂歌曲及評論資訊Python爬蟲
- python爬蟲實戰,爬蟲之路,永無止境Python爬蟲
- 圖靈樣書爬蟲 - Python 爬蟲實戰圖靈爬蟲Python
- Java爬蟲-爬取疫苗批次資訊Java爬蟲
- 爬蟲實戰——58同城租房資料爬取爬蟲
- python爬蟲實戰:爬取西刺代理的代理ip(二)Python爬蟲
- Python爬蟲實戰-使用Scrapy框架爬取土巴兔(一)Python爬蟲框架
- 實戰 | 用aiohttp和uvloop實現一個高效能爬蟲AIHTTPOOP爬蟲
- Python 爬蟲實戰(2):股票資料定向爬蟲Python爬蟲
- Python網路爬蟲(正則, 內涵段子,貓眼電影, 鏈家爬取)Python爬蟲
- 【Python爬蟲9】Python網路爬蟲例項實戰Python爬蟲
- python爬蟲學習(4)抓取鏈家網二手房資料Python爬蟲
- python爬蟲實戰教程-Python爬蟲開發實戰教程(微課版)Python爬蟲
- python爬蟲---網頁爬蟲,圖片爬蟲,文章爬蟲,Python爬蟲爬取新聞網站新聞Python爬蟲網頁網站
- Python實現微博爬蟲,爬取新浪微博Python爬蟲
- 爬蟲——爬取貴陽房價(Python實現)爬蟲Python
- 網路爬蟲——爬蟲實戰(一)爬蟲
- 爬蟲實戰爬蟲
- 【實戰】使用asyncio爬取gitbook內容輸出pdfGit
- 想提高爬蟲效率?aiohttp 瞭解下爬蟲AIHTTP