豆瓣的反爬虫机制确实比较严格,直接硬刚很容易被封 IP。但别慌,作为一名老爬虫,我来分享一些经验,教你如何更高效、更安全地爬取豆瓣电影数据,同时尽量避免被封 IP。
一、了解豆瓣的反爬机制
在开始之前,我们需要先了解豆瓣常见的反爬虫手段,才能更好地制定应对策略:
- User-Agent 检测: 检查请求头的 User-Agent 字段,如果不是常见的浏览器 User-Agent,可能会被拒绝访问。
- IP 访问频率限制: 同一个 IP 在短时间内访问过于频繁,会被限制访问或封禁。
- Cookie 验证: 某些页面需要登录才能访问,或者需要携带特定的 Cookie 才能正常访问。
- 验证码: 遇到异常请求时,可能会弹出验证码。
- 数据加密: 某些数据可能经过加密处理,需要解密才能使用。
- 动态加载: 页面内容通过 JavaScript 动态加载,需要执行 JavaScript 才能获取完整数据。
二、应对策略
针对以上反爬机制,我们可以采取以下策略:
- 伪装 User-Agent:
这是最基本的操作。每次发起请求时,随机从一个 User-Agent 池中选择一个 User-Agent,模拟真实浏览器的行为。
import random
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0',
]
def get_random_user_agent():
return random.choice(user_agents)
headers = {
'User-Agent': get_random_user_agent()
}
- 使用代理 IP:
这是避免 IP 被封的最有效方法。使用大量的代理 IP,每次请求随机选择一个代理 IP,分散访问压力。
- 获取代理 IP: 可以从一些免费或付费的代理 IP 网站获取。
- 验证代理 IP: 获取到的代理 IP 可能不稳定,需要验证其可用性。可以使用
requests
库测试代理 IP 是否可用。 - 维护代理 IP 池: 定期检查代理 IP 的可用性,移除失效的代理 IP。
import requests
proxies = [
{'http': 'http://10.10.1.10:3128'},
{'http': 'http://10.10.1.11:1080'},
]
def test_proxy(proxy):
try:
response = requests.get('https://www.douban.com', proxies=proxy, timeout=5)
if response.status_code == 200:
print(f'代理 {proxy} 可用')
return True
else:
print(f'代理 {proxy} 不可用,状态码:{response.status_code}')
return False
except Exception as e:
print(f'代理 {proxy} 连接失败:{e}')
return False
- 控制访问频率:
不要过于频繁地访问豆瓣。可以设置一个合理的访问间隔,例如每隔几秒或十几秒访问一次。
import time
def crawl_data(url):
# ...
time.sleep(random.uniform(3, 5)) # 随机休眠 3-5 秒
# ...
- 使用 Cookie 登录:
某些页面需要登录才能访问,可以先手动登录豆瓣,然后将 Cookie 保存下来,在爬虫中使用 Cookie 发起请求。
cookies = {
'bid': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
# ... 其他 Cookie
}
response = requests.get(url, headers=headers, cookies=cookies)
- 处理验证码:
如果遇到验证码,可以尝试以下方法:
- 手动输入验证码: 对于少量数据,可以手动输入验证码。
- 使用验证码识别 API: 可以使用一些第三方验证码识别 API,自动识别验证码。
- 解析动态加载的内容:
如果页面内容通过 JavaScript 动态加载,可以使用以下方法:
- Selenium: 使用 Selenium 模拟浏览器行为,执行 JavaScript,获取渲染后的页面内容。Selenium 的缺点是速度较慢。
- Pyppeteer: Pyppeteer 是 Puppeteer 的 Python 版本,也可以用来执行 JavaScript。Pyppeteer 比 Selenium 更轻量级,速度更快。
- 分析 Ajax 请求: 找到加载数据的 Ajax 请求,直接请求 Ajax 接口,获取数据。这种方法效率最高,但需要分析网页的 JavaScript 代码。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get(url)
html = driver.page_source
driver.quit()
- 分布式爬取:
如果需要爬取的数据量非常大,可以考虑使用分布式爬取。将爬虫程序部署到多台机器上,同时进行爬取,可以大大提高爬取效率。需要注意,每台机器都需要使用不同的代理IP,避免被豆瓣识别为同一个用户。
三、实战案例:爬取豆瓣电影 Top250
下面是一个简单的爬取豆瓣电影 Top250 的示例代码:
import requests
from bs4 import BeautifulSoup
import random
import time
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0',
]
proxies = [
# 填写你的代理IP
]
def get_random_user_agent():
return random.choice(user_agents)
def get_random_proxy():
if proxies:
return random.choice(proxies)
else:
return None
def crawl_douban_top250(page):
url = f'https://movie.douban.com/top250?start={page * 25}&filter='
headers = {
'User-Agent': get_random_user_agent()
}
proxy = get_random_proxy()
if proxy:
try:
response = requests.get(url, headers=headers, proxies=proxy, timeout=10)
except:
print("代理出错,切换IP")
return
else:
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
movies = soup.find_all('div', class_='item')
for movie in movies:
title = movie.find('span', class_='title').text
rating = movie.find('span', class_='rating_num').text
print(f'电影名:{title},评分:{rating}')
else:
print(f'请求失败,状态码:{response.status_code}')
time.sleep(random.uniform(3, 5))
if __name__ == '__main__':
for page in range(10):
crawl_douban_top250(page)
注意:
- 请务必遵守豆瓣的 robots.txt 协议,不要爬取不允许爬取的页面。
- 请合理控制访问频率,避免对豆瓣服务器造成过大的压力。
- 请勿将爬取到的数据用于商业用途。
- 示例代码中的
proxies
列表需要填写你自己的代理 IP。
四、总结
爬取豆瓣电影数据需要一定的技巧,但只要掌握了以上策略,就可以更高效、更安全地获取数据,同时避免被封 IP。记住,爬虫的本质是模拟用户行为,所以要尽量让你的爬虫看起来像一个真实用户。希望这些技巧能帮助你成功构建自己的电影推荐系统!