urllib2.urlopen请求IMDB影片演职人员页面返回空内容的问题求助
问题描述
我之前用urllib2读取IMDB页面一直正常,但最近请求影片演职人员页面时,urllib2.urlopen返回的响应是空的——没有报错,但逐行读取时循环执行0次。不过在浏览器里打开该页面完全正常,甚至在开发者工具里能看到“刚开始返回空白,等待后才出现完整源码”的现象。
我的测试代码如下:
import urllib2 # 注意:原代码遗漏了ssl模块导入,会导致隐性错误 import ssl def openConnection(URL): try: req = urllib2.Request(URL, headers={'User-Agent' : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"}) context = ssl._create_unverified_context() con = urllib2.urlopen( req, context=context ) # 测试用的逐行打印 for line in con: print line return con except: # 宽泛的异常捕获吞掉了真实错误信息 print "Connection failed. Retrying." def readIMDB(movie): data = openConnection("https://www.imdb.com/title/" + movie + "/fullcredits") readIMDB("tt0084726")
其他URL传入openConnection都能正常逐行打印内容,但IMDB的这个演职人员页面就是不行。想请教:为什么会出现这种情况?有没有办法修改代码拿到数据?
问题根源分析
结合IMDB的页面机制和urllib2的局限性,主要有这几个核心原因:
- 动态内容渲染(最可能)
IMDB的演职人员页面现在大量内容是通过JavaScript动态加载的。你在浏览器里看到“等待后才出现源码”的现象,本质是浏览器在执行页面中的JS脚本,动态拉取并渲染实际的演职人员数据。
而urllib2只是纯HTTP请求工具,它只能获取服务器返回的初始静态响应,不会执行任何JavaScript代码。如果IMDB返回的初始响应里只有页面框架(没有实际的演职人员HTML),urllib2自然读不到有效内容,看起来就像返回了空。
响应压缩未处理
IMDB服务器通常会返回gzip压缩后的响应来节省带宽。urllib2默认不会自动解压gzip内容,此时直接逐行读取响应时,因为压缩数据的二进制格式没有换行符,会导致for line in con循环执行0次(没有可识别的“行”)。隐性错误被吞掉
你的代码里except块太宽泛,没有捕获并打印具体异常信息——比如可能存在SSL验证的隐性问题、请求被IMDB反爬机制拦截(虽然加了UA,但可能还有其他检测),但这些错误被笼统的except覆盖了,你看不到真实问题。
解决方案
针对不同原因,给出对应的修复方案:
方案1:先处理响应压缩(快速验证)
先尝试解决压缩问题,这是最容易验证的:
import urllib2 import ssl import gzip from StringIO import StringIO # Python 2.x用这个,Python3请用io.BytesIO def openConnection(URL): try: req = urllib2.Request(URL, headers={ 'User-Agent' : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", 'Accept-Encoding': 'gzip, deflate' # 明确告诉服务器我们接受压缩响应 }) context = ssl._create_unverified_context() con = urllib2.urlopen(req, context=context) # 检查并解压gzip响应 content_encoding = con.info().get('Content-Encoding') if content_encoding == 'gzip': buf = StringIO(con.read()) f = gzip.GzipFile(fileobj=buf) content = f.read() print(content) return content else: # 正常逐行读取 for line in con: print line return con except Exception as e: # 打印具体错误信息,方便排查 print "Connection failed. Error: {} - {}".format(type(e).__name__, str(e))
如果修改后能看到内容,说明是压缩问题;如果还是空,那大概率是动态渲染的问题。
方案2:用支持JS渲染的工具(解决动态内容问题)
如果是动态渲染导致的,urllib2这类纯HTTP工具就不够用了,需要用能模拟浏览器执行JS的工具,比如selenium:
from selenium import webdriver from selenium.webdriver.chrome.options import Options def readIMDB(movie): # 配置无头浏览器(不弹出可视化窗口) chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36") driver = webdriver.Chrome(options=chrome_options) try: url = "https://www.imdb.com/title/" + movie + "/fullcredits" driver.get(url) # 等待页面动态内容加载完成(隐式等待10秒) driver.implicitly_wait(10) # 获取和浏览器一致的完整页面源码 page_source = driver.page_source print(page_source) return page_source finally: # 确保浏览器进程关闭 driver.quit() readIMDB("tt0084726")
selenium会模拟真实浏览器的行为,执行页面中的JS,所以能拿到和浏览器里一样的完整内容。
方案3:优化错误捕获,排查隐性问题
修改except块,打印具体异常信息,快速定位是否有反爬或SSL问题:
except Exception as e: print "Connection failed. Error type: {}, Message: {}".format(type(e).__name__, str(e))
比如如果IMDB返回了403 Forbidden,你就能看到对应的错误,这时候可能需要添加更多请求头(比如Accept、Referer),或者使用代理、添加Cookie来绕过反爬。
额外提醒
IMDB有官方API,如果你是合法的爬取需求,建议优先使用官方API,这样更稳定,也不会触发反爬机制。不过官方API可能需要申请密钥,部分功能可能收费,但比自己爬取页面更可靠。




