asyncio实现的爬虫函数返回null/空列表组成的列表,是什么原因?
1. 异常分支无返回值,导致空输入传入解析逻辑
get_response方法只有请求成功时返回响应文本,触发异常时仅打印错误无返回值,默认返回None。parse方法接收到None作为输入时,BeautifulSoup会生成空结构,查找script标签就会返回空列表。
2. 信号量未实际生效,并发过高触发反爬
你当前的BoundedSemaphore用法完全错误:直接放在main方法的async with块下包裹gather逻辑,等于没有对单个请求做并发限制,17000个请求会几乎同时发出,Netflix的反爬机制会直接拦截你的IP,返回反爬验证页、403/429错误页,这些页面不存在你要匹配的内容。
正确用法是将信号量传入response_soup方法,包裹请求逻辑:
async def response_soup(self, session, semaphore, url): async with semaphore: html = await self.get_response(session, url) # 后续逻辑不变
main方法中创建任务时传入信号量:
async def main(self, urls): semaphore = asyncio.BoundedSemaphore(3) async with aiohttp.ClientSession() as session: results = await asyncio.gather(*[self.response_soup(session, semaphore, url) for url in urls]) # 后续逻辑不变
3. 未加请求头,请求被直接拦截
默认aiohttp的请求头会暴露你是脚本请求,Netflix会直接拒绝这类请求,你需要在session.get时添加合法的User-Agent等请求头,模拟浏览器行为:
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" } # 请求时传入headers async with session.get(url=url, headers=headers) as response:
4. 缺少响应状态校验,错误内容直接进入解析
你没有判断响应的状态码,即使返回403、404、429等错误状态,依然会把响应内容传给解析逻辑,而这些错误页不存在你要匹配的error-page not-found类名,也不存在目标script标签,最终返回空列表。
需要在get_response中添加状态码判断:
async with session.get(url=url, headers=headers) as response: if response.status != 200: print(f"Url {url} return status code {response.status}") return None print(f'Successfully got url {url}.') return await response.text()
5. URL列表存在格式错误
你给出的URL列表第一个地址末尾多了一个英文句号,属于无效URL,请求会直接失败。
6. 解析逻辑覆盖场景不全
Netflix的地区限制页、登录提示页、反爬验证页都不会携带error-page not-found类名,你当前的判断逻辑无法识别这类页面,会直接查找script标签,返回空列表。可以增加更多页面特征判断,或者直接判断目标script是否存在再返回。
内容的提问来源于stack exchange,提问作者tadeo soresi

