
在编写代码之前,理解我们的“对手”至关重要。JSP站点通常通过以下几种方式识别和拦截爬虫:
requests库默认的User-Agent会直接暴露爬虫身份。JSESSIONID等Cookie来维持用户会话。不处理Cookie,就无法保持登录状态或通过某些验证流程。Referer字段,确保请求来源于站内页面,而非直接访问。requests库。我们的目标是将一个赤裸的HTTP请求,包装成一个由真实浏览器发出的、可信的请求。
这是伪装的第一步,也是最关键的一步。一个真实的浏览器请求头包含丰富的信息。
关键字段:
User-Agent: 标识操作系统和浏览器类型。Referer: 表明当前请求是从哪个页面链接过来的。Accept: 声明客户端能接收的内容类型。Accept-Language: 声明浏览器接受的语言。Connection: 保持连接。使用requests.Session()对象。它会自动处理Cookie,在多次请求间保持会话状态,就像浏览器一样。
在请求间引入随机延时,模拟人类阅读和点击的间隔。使用time.sleep()。
当简单的请求头伪装无效时,可能是遇到了JavaScript挑战。此时需要动用Selenium或Playwright等浏览器自动化工具,它们能驱动真实浏览器内核(如Chrome)执行页面上的所有JavaScript代码。
假设我们的目标是爬取一个名为 http://example-jsp-site.com/gallery.jsp 的图片画廊。
import requests
import time
import random
from bs4 import BeautifulSoup
# 定义一个常见的浏览器User-Agent列表,用于随机选择
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 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
]
# 创建一个会话对象
session = requests.Session()
# 为目标URL构造一个看起来真实的请求头
headers = {
'User-Agent': random.choice(USER_AGENTS),
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
session.headers.update(headers)
try:
# 首先访问主画廊页面,Referer可以是搜索引擎或站内首页
gallery_url = "http://example-jsp-site.com/gallery.jsp"
response = session.get(gallery_url, timeout=10)
response.raise_for_status() # 如果状态码不是200,则抛出异常
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(response.text, 'html.parser')
# 假设图片链接在 <img> 标签的 `src` 属性中,且位于class为'gallery-img'的div下
image_elements = soup.select('div.gallery-img img')
for index, img in enumerate(image_elements):
# 构造完整的图片URL(处理相对路径)
img_src = img.get('src')
if img_src.startswith('/'):
img_url = "http://example-jsp-site.com" + img_src
else:
img_url = img_src
# 为图片请求设置Referer,表明是从gallery.jsp页面来的
img_headers = {'Referer': gallery_url}
# 请求图片内容
print(f"正在下载图片 {index+1}: {img_url}")
img_response = session.get(img_url, headers=img_headers)
img_response.raise_for_status()
# 将图片保存到本地
with open(f'image_{index+1}.jpg', 'wb') as f:
f.write(img_response.content)
# !!! 重要:在请求间添加随机延时,模拟人类行为 !!!
sleep_time = random.uniform(1, 3) # 随机等待1-3秒
time.sleep(sleep_time)
except requests.exceptions.RequestException as e:
print(f"网络请求出错: {e}")如果目标站点必须执行JavaScript才能加载内容,requests就无能为力了。这时需要Selenium。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import os
# 代理配置信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
# 配置Chrome选项,使其更接近真实用户
chrome_options = Options()
# 设置代理服务器
proxy_url = f"{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
chrome_options.add_argument(f'--proxy-server=http://{proxy_url}')
# 可选:无头模式(不显示浏览器界面)
# chrome_options.add_argument("--headless")
# 设置一个常见的用户代理
chrome_options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
# 禁用自动化测试标志,避免被检测为WebDriver
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
# 对于需要认证的代理,可以安装扩展来处理认证
from selenium.webdriver.chrome.options import Options as ChromeOptions
# 初始化WebDriver
driver = webdriver.Chrome(options=chrome_options) # 请确保chromedriver在PATH中
# 处理代理认证的替代方案(如果上面的方法不工作)
def enable_proxy_auth(proxy_host, proxy_port, proxy_username, proxy_password):
"""
通过执行JavaScript来设置代理认证
这是一个备选方案
"""
from selenium.webdriver.common.proxy import Proxy, ProxyType
proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = f"{proxy_host}:{proxy_port}"
proxy.ssl_proxy = f"{proxy_host}:{proxy_port}"
# 创建代理认证扩展
auth_extension_path = create_proxy_auth_extension(proxy_host, proxy_port, proxy_username, proxy_password)
if auth_extension_path:
chrome_options.add_extension(auth_extension_path)
def create_proxy_auth_extension(proxy_host, proxy_port, proxy_username, proxy_password):
"""
创建代理认证扩展
"""
import zipfile
import os
import string
import random
# 生成扩展的manifest.json
manifest_json = """
{
"version": "1.0.0",
"manifest_version": 2,
"name": "Chrome Proxy",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}
"""
# 生成背景脚本
background_js = """
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "http",
host: "%s",
port: parseInt(%s)
},
bypassList: ["localhost"]
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
return {
authCredentials: {
username: "%s",
password: "%s"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
""" % (proxy_host, proxy_port, proxy_username, proxy_password)
# 创建临时扩展文件
extension_dir = 'proxy_auth_extension'
if not os.path.exists(extension_dir):
os.makedirs(extension_dir)
with open(os.path.join(extension_dir, "manifest.json"), "w") as f:
f.write(manifest_json)
with open(os.path.join(extension_dir, "background.js"), "w") as f:
f.write(background_js)
# 创建ZIP文件
extension_path = os.path.join(extension_dir, "extension.zip")
with zipfile.ZipFile(extension_path, 'w') as zp:
zp.write(os.path.join(extension_dir, "manifest.json"), "manifest.json")
zp.write(os.path.join(extension_dir, "background.js"), "background.js")
return extension_path
# 如果简单代理设置不工作,使用扩展方式
# enable_proxy_auth(proxyHost, proxyPort, proxyUser, proxyPass)
try:
# 访问目标页面
print("正在通过代理访问目标页面...")
driver.get("http://example-jsp-site.com/gallery.jsp")
# 使用显式等待,等待图片容器加载完成,而不是使用固定的time.sleep
wait = WebDriverWait(driver, 10)
# 假设图片加载在一个id为'imageContainer'的元素里
image_container = wait.until(EC.presence_of_element_located((By.ID, "imageContainer")))
# 在页面中执行JavaScript,模拟滚动以确保所有懒加载图片都被触发
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # 等待滚动后加载
# 查找所有图片元素
img_elements = driver.find_elements(By.CSS_SELECTOR, "div.gallery-img img")
# 创建目录保存图片
if not os.path.exists('selenium_images'):
os.makedirs('selenium_images')
print(f"找到 {len(img_elements)} 张图片")
for index, img in enumerate(img_elements):
img_url = img.get_attribute('src')
print(f"通过Selenium获取到图片链接 {index+1}: {img_url}")
# 为了下载,我们可以使用requests会话,但需要传递Selenium获得的Cookie
# 或者,也可以直接通过Selenium截图,但这里演示用requests下载(更高效)
# 注意:通过Selenium获取的链接可能是动态加载的,直接用requests下载时可能需要保持相同的会话。
# 更稳妥的方法是继续使用Selenium来处理,或者将Cookie从Selenium传递给requests会话。
except Exception as e:
print(f"发生错误: {e}")
finally:
# 关闭浏览器
print("爬取完成,关闭浏览器...")
driver.quit()
# 清理临时扩展文件
import shutil
if os.path.exists('proxy_auth_extension'):
shutil.rmtree('proxy_auth_extension')通过上述策略和代码,我们已经能够成功模拟一个正常浏览器对JSP站点的访问。我们来总结一下核心步骤:
User-Agent和其他头部信息。Session对象自动处理Cookies。Selenium或Playwright。robots.txt:在爬取前,检查目标网站的/robots.txt文件,尊重网站管理员的意愿。技术伦理提醒:爬虫技术是一把双刃剑。在实践过程中,请务必:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。