使用asyncio抓取cian站点经纪人信息遇两类问题求助
Asyncio抓取CIAN站点的问题排查
问题1:异步请求触发VPN验证,同步请求正常
使用aiohttp的全异步请求时,收到站点要求使用VPN的提示,但改用同步urlopen后能正常获取数据。
异步请求代码:
async def async_get_all_realtors(session, url): async with session.get(url) as response: html = await response.text() bsobj = soup(html, 'html.parser') agent_names = [] for i in bsobj.findAll('div', {'class': '_9400a595a7--name--ipaRv'}): agent_names.append(i.span.text.strip()) return agent_names
修改后的同步请求代码:
async def async_get_all_realtors(url): html = urlopen(url) bsobj = soup(html, 'html.parser') agent_names = [] for i in bsobj.findAll('div', {'class': '_9400a595a7--name--ipaRv'}): agent_names.append(i.span.text.strip()) return agent_names
原因与解决方案
站点反爬机制识别了aiohttp的默认请求特征(如缺少浏览器标识的User-Agent),判定为异常请求。解决方法:
- 给异步请求添加模拟浏览器的请求头,示例:
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' } async with session.get(url, headers=headers) as response: - 可适当添加请求延迟,避免短时间内发起大量请求触发反爬限制。
问题2:Main函数内打印结果正常,外部await返回空值
完整代码中,main函数内打印all_agent_names显示数据完整,但外部通过await main(url, num_of_pages)获取的结果为空。
完整代码:
from urllib.request import urlopen import aiohttp from bs4 import BeautifulSoup as soup import asyncio import pandas as pd url = "https://www.cian.ru/realtors/?dealType=sale&offerType%5B0%5D=flat®ionId=1" def get_num_pages(url): html = urlopen(f"{url}&page=1") bsobj = soup(html.read(), 'html.parser') return int(bsobj.findAll('span', {'class': '_9400a595a7--content--sGuO7'})[-1].text) async def async_get_all_realtors(url): html = urlopen(url) bsobj = soup(html, 'html.parser') agent_names = [] for i in bsobj.findAll('div', {'class': '_9400a595a7--name--ipaRv'}): agent_names.append(i.span.text.strip()) return agent_names async def main(url, num_pages): tasks = [] for page_num in range(1, num_pages + 1): page_url = f"{url}&page={page_num}" task = asyncio.create_task(async_get_all_realtors(page_url)) tasks.append(task) results = await asyncio.gather(*tasks) # Here all are Ok this variable contains full information all_agent_names = [name for result in results for name in result] print(all_agent_names) return all_agent_names num_of_pages = get_num_pages(url) result = await main(url, num_of_pages) # Empty result, why?
原因与解决方案
- 同步阻塞事件循环:在异步函数
async_get_all_realtors中使用同步的urlopen,会阻塞整个asyncio事件循环,并发执行时可能导致任务执行异常,虽然main内的print在任务完成后能输出结果,但返回值可能因事件循环状态异常丢失。 - Jupyter事件循环特性:Jupyter Notebook自带事件循环,直接使用
await可能和任务创建的事件循环存在冲突。
解决方法:
- 替换同步
urlopen为aiohttp的异步请求(结合问题1的请求头设置),避免阻塞事件循环。 - 在Jupyter中改用
asyncio.run()执行主函数,或手动管理事件循环:
或者:# 替代直接await main(...) result = asyncio.run(main(url, num_of_pages))import asyncio try: loop = asyncio.get_running_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) result = loop.run_until_complete(main(url, num_of_pages)) - 确保单元格只执行一次
result = await main(...),避免变量被重复赋值或事件循环冲突。
内容的提问来源于stack exchange,提问作者Roman Kazmin
相关产品推荐
相关产品推荐

