日本动态ip代理爬虫实战记录:从选池到稳定抓取的完整链路

为什么这次我盯着日本节点做了一整周
上个月接了个活儿,帮一个做选品的团队抓日本某头部电商平台的商品详情页和价格变动数据。需求不复杂,但坑不少。日本站点的反爬策略跟国内完全不是一个路数,它不靠验证码拦你,而是靠IP信誉评分和请求行为指纹来判定你是不是”真人”。你用一个数据中心IP连过去,大概率第一页就能拿到,第二页开始就给你返回空壳或者直接403。更烦的是,日本那边对IP的”地域一致性”要求很严,你IP显示在东京,但请求头里的语言、时区、Accept-Language对不上,它也能给你标记成异常流量。
所以这次我全程用的日本动态住宅IP代理,从选池、配参数、写代码到跑稳定,前前后后折腾了大概六天。下面把整个过程拆开来讲,算是给自己留个记录,也希望能帮到同样在搞日本站点抓取的朋友。
选池这件事,别上来就开跑
很多人拿到代理账号第一件事就是写脚本开抓,结果跑了两百个请求IP就被拉黑了。问题出在哪?出在你没搞清楚自己到底需要什么样的IP池。
日本动态IP代理选池,我一般看四个维度:
第一,IP来源必须是真实住宅。日本站点对数据中心IP的识别率非常高,你用一个AWS东京机房的IP去请求,基本活不过三轮。住宅IP走的是日本本地家庭宽带出口,在对方风控系统里天然就是”普通用户”,这个优势是数据中心IP给不了的。
第二,城市级定位要能锁到。日本电商很多页面会根据IP归属城市展示不同价格或库存。如果你需要模拟东京用户的行为,IP就得落在东京都,不能是神奈川或者大阪。选池的时候确认一下服务商能不能做到城市级精准定位,这个差别在实际抓取中非常关键。
第三,IP池的更新频率和去重机制。动态IP不是给你分配一个IP用到底,而是按会话周期轮换。如果池子小、去重做得差,你跑着跑着就会反复遇到同一个IP,对方一标记你就全完了。我这次用的池子有9000万+的住宅IP资源,覆盖200多个国家,日本节点的占比和更新频率都够用,跑了一整天没出现重复IP的情况。
第四,会话时长要能自定义。抓一个商品详情页可能30秒就完了,但如果你要模拟用户浏览、加购、看评价这一整套流程,会话得保持3到5分钟。会话时长太短,你刚加载完列表页IP就换了,行为链断了,反而更容易被识别。我这次把会话时长设在了5分钟,刚好覆盖一次完整的浏览路径。
把这几个维度对齐之后,我最终选的是网帆代理的动态住宅IP池(全面型),按流量计费,日本节点城市级定位,会话时长5分钟,HTTP/HTTPS协议。这个组合在成本和稳定性之间平衡得比较好,适合中小规模的抓取任务。需要特别说明的是,网帆代理的海外代理套餐仅适用于中国大陆以外地区,大陆网络环境无法直接使用。
连接配置:几个容易踩坑的参数
拿到代理之后,别急着写业务逻辑,先把连接层调通。日本节点有几个参数我反复调了好几轮才稳定:
协议选择:日本电商站点现在基本全走HTTPS,所以代理协议必须支持HTTPS。网帆代理这边HTTP/HTTPS/SOCKS5都兼容,我用的HTTPS直连,省了一层SOCKS转发的延迟。
请求头模拟:这个比很多人想象的重要。日本站点的反爬会校验User-Agent、Accept-Language、Accept-Encoding这几个头。你的IP是日本的,但UA写的是”Python-requests/2.28″,Accept-Language写的是”zh-CN”,这组合一出来基本就废了。我后来统一用了一组固定的日本浏览器指纹:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8",
"Accept-Language": "ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Cache-Control": "max-age=0",
"sec-ch-ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"'
}
时区处理:日本是UTC+9,如果你的服务器跑在UTC+8或者UTC+0,请求里带的时间戳跟IP归属地时区对不上,也是减分项。我在代码里统一把时区锁到了Asia/Tokyo。
请求间隔:别用固定间隔。我这次用的是2.5秒到6秒之间的随机间隔,偶尔穿插一个8到12秒的”长停顿”,模拟人看页面的节奏。固定间隔是最容易被行为分析模型抓到的特征之一。
实战代码:从列表页到详情页的完整链路
下面这段代码是我实际跑通的核心逻辑,做了简化处理,去掉了具体的站点URL和字段解析部分,但代理接入、会话管理、异常重试这些关键环节都保留了:
import requests
import random
import time
import logging
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
class JapanScraper:
def __init__(self, proxy_host, proxy_port, proxy_user, proxy_pass):
self.session = requests.Session()
self.session.proxies = {
"http": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}",
"https": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
}
重试策略:最多重试3次,针对429和5xx
retry_strategy = Retry(
total=3,
backoff_factor=1.5,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET"]
)
adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=10, pool_maxsize=10)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept-Language": "ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
})
def random_delay(self, min_s=2.5, max_s=6.0):
"""随机延迟,偶尔插入长停顿"""
if random.random() 详情页"""
all_products = []
for page in range(1, max_pages + 1):
links = self.fetch_list_page(base_url, page)
if not links:
logger.info(f"第{page}页无数据,停止翻页")
break
for link in links:
self.random_delay()
detail = self.fetch_detail_page(link)
if detail:
all_products.append(detail)
self.random_delay(min_s=3, max_s=8)
logger.info(f"已完成第{page}页,累计采集{len(all_products)}条")
return all_products
def _parse_product_links(self, html):
"""占位:实际项目中解析商品链接"""
return []
def _parse_product_detail(self, html):
"""占位:实际项目中解析商品详情字段"""
return {}
# 使用示例
if __name__ == "__main__":
scraper = JapanScraper(
proxy_host="jp-proxy.fanproxy.com", 替换为你实际分配的代理地址
proxy_port=8080,
proxy_user="your_username",
proxy_pass="your_password"
)
results = scraper.run("https://example-jp-site.com/products", max_pages=15)
logger.info(f"任务完成,共采集 {len(results)} 条商品数据")
几个代码里的细节值得说一下。第一,Session对象是复用的,不要每次请求都新建一个连接,TCP握手和TLS握手的开销在日本节点上大概多200到400毫秒,累积起来很可观。第二,重试策略里我特意把429(请求过于频繁)加进去了,日本站点在限流时经常返回429而不是直接403,如果你不处理这个状态码,重试逻辑就形同虚设。第三,列表页和详情页之间我加了随机延迟,详情页和下一个详情页之间也加了,这个节奏感很重要。
跑了一整天之后,我做了哪些稳定性调优
第一天跑的时候,大概每40到50个请求就会遇到一次403或者返回空页面。排查下来主要是三个原因,我逐个解决的:
问题一:IP被短期标记。日本站点的IP信誉系统不是永久封禁,而是有一个”观察期”,大概10到15分钟。你被标记之后,这个IP在观察期内继续用就会一直403。我的处理方式是:一旦连续两次收到403,立刻丢弃当前会话,重新获取一个新IP,而不是在同一个IP上死磕。网帆代理的动态住宅池支持自动轮换,我这边只需要断开当前session重新建立连接就行,新IP一般3秒内就能拿到。
问题二:请求频率在”看起来正常”的范围内但依然被拦。后来我发现日本站点的行为分析不是单纯看QPS,它还会看页面停留时间和滚动深度。你3秒翻完一个列表页、2秒跳到详情页、1秒又跳回来,这个行为模式跟真人差距太大了。我后来把列表页到详情页的间隔拉长到了4到9秒,并且在详情页停留至少2到4秒再发起下一个请求。改完之后403率从大概8%降到了1%以下。
问题三:TLS指纹暴露。Python的requests库底层用的是urllib3,它的TLS握手特征跟真实浏览器有明显差异。日本一些站点的WAF会做TLS指纹检测(JA3/JA4)。我这次没有上mitmproxy或者curl_cffi那套重方案,而是把请求频率降下来、UA和请求头做齐,实际效果已经够用了。如果你的目标站点TLS检测特别严,可以考虑用curl_cffi库来模拟Chrome的TLS指纹,这里不展开。
调优之后,我连续跑了14个小时,采集了大约3200条商品数据,403率稳定在1%以内,超时率不到0.5%,整体成功率在98.5%以上。这个数据对于日本站点的抓取来说已经算比较理想了。
成本怎么算:按带宽和按流量到底选哪个
这次任务跑下来,我实际消耗了大概18GB的流量(主要是商品详情页的HTML和少量图片资源)。如果按流量计费,成本是可控的。但如果你要抓的是视频类内容或者大文件,流量消耗会非常夸张,这时候按带宽计费的模式就划算很多。
我整理了一个简单的对比,供参考:
| 对比维度 | 按流量计费(动态住宅) | 按带宽计费(动态不限量) |
|---|---|---|
| 计费方式 | 按实际消耗的GB数结算 | 按峰值带宽结算,流量和IP调用次数不限 |
| 适合场景 | 中小规模抓取、流量可预估的任务 | 高并发、长时间连续运行、流量不可控的任务 |
| IP资源 | 9000万+住宅IP池,城市级定位 | 200+国家住宅IP,100Gbps+高带宽 |
| 会话时长 | 可自定义 | 3-60分钟自定义,支持自动轮换 |
| 成本特征 | 用量小成本低,用量大成本线性增长 | 固定带宽成本,用量越大单位成本越低 |
我这次的任务属于”流量可预估、规模中等”的类型,所以选了按流量计费的动态住宅方案。但如果你要同时跑几十个线程、连续跑一周以上,或者抓的内容包含大量图片视频,那动态不限量的按带宽模式在成本上会明显更优,而且不限流量和IP调用次数,跑起来不用盯着流量面板算钱。
几个我踩过的坑,提前帮你避掉
坑一:代理IP和服务器时区不一致。我一开始服务器跑在阿里云新加坡(UTC+8),代理IP是日本(UTC+9),请求里带的时间戳差了1个小时。日本站点的日志系统对时间戳很敏感,这个差异虽然不会直接导致403,但会影响IP信誉评分。后来我把服务器时区统一改成了Asia/Tokyo,问题就消失了。
坑二:Cookie没有跨请求保持。日本电商在第一次访问时会种一个session cookie,后续请求如果没带上这个cookie,会被当成”每次都是新访客”,触发更严格的风控。我的Session对象天然会保持cookie,但如果你用的是多线程或者多进程架构,要注意每个线程/进程用独立的Session,不要共享,否则cookie会串。
坑三:代理连接池没设上限。requests的默认连接池是10个连接,如果你开了50个线程,连接池会频繁创建和销毁TCP连接,在日本节点上这个开销不小。我在HTTPAdapter里把pool_connections和pool_maxsize都设到了20,线程数控制在15以内,连接复用率明显提升,平均响应时间从1.8秒降到了1.1秒左右。
常见问题
Q1:日本动态IP代理的IP质量怎么判断?拿到手之后怎么快速验证?
拿到代理之后,我一般做三步验证。第一步,用curl直接请求日本IP查询接口,确认返回的IP归属地确实是日本,并且城市跟你要求的一致。第二步,连续请求20次,看每次返回的IP是不是都不同(动态IP应该每次给不同的),同时记录响应时间,日本节点正常应该在80到200毫秒之间,如果超过300毫秒说明链路有问题。第三步,拿目标站点的一个公开页面实际请求一次,看能不能正常返回200,如果直接403或者返回空壳,说明这个IP池的信誉度不够,需要联系服务商换一批。网帆代理这边有99.9%的流量高峰段成功率保障,而且支持实时去重净化,自动筛除异常节点,一般拿到手就能用,不用自己反复测。
Q2:跑着跑着突然大面积403,是IP池的问题还是我代码的问题?怎么快速定位?
先别急着改代码。你打开日志,看403是突然集中爆发还是零星出现。如果是突然集中爆发(比如之前一直正常,突然连续10个请求都403),大概率是IP池里有一批IP被目标站点集中标记了,这时候你重新获取新IP、等10到15分钟再跑,基本就恢复了。如果是零星出现(每50个请求里偶尔一两个403),那更可能是你的请求行为模式触发了风控,检查一下请求间隔是不是太规律、请求头是不是有遗漏、TLS指纹是不是暴露了。还有一种情况是你同时用了多个IP但请求模式完全一样,对方会把这组IP关联起来一起标记,这时候需要给每个IP分配不同的请求节奏和请求头组合。
Q3:会话时长设多长比较合适?设太短和设太长分别有什么影响?
这个没有标准答案,取决于你的业务场景。如果你只是抓单个页面的数据,3到5分钟的会话时长足够了,一个IP完成2到3个请求就换,IP利用率低但安全性高。如果你要模拟完整的用户浏览路径(列表页→详情页→评价页→返回),建议设5到10分钟,保证行为链完整。设太短(比如1分钟)的话,你刚打开列表页IP就换了,详情页用的是另一个IP,行为链断了,反而容易被识别为异常。设太长(比如30分钟以上)的话,单个IP被使用的请求数太多,被标记的概率会上升。我这次用的5分钟是一个比较稳妥的中间值,网帆代理的会话时长支持3到60分钟自定义,你可以根据自己的任务节奏灵活调整。
最后说两句
日本站点的抓取,核心就八个字:IP要真,行为要像。IP不真,你代码写得再漂亮也白搭;行为不像,IP再干净也扛不住。动态住宅IP解决的是”IP要真”的问题,而请求节奏、请求头、时区、TLS指纹这些细节解决的是”行为要像”的问题。两边都做到位了,稳定跑个一两周不是问题。
这次用的代理是网帆代理的动态住宅IP方案,9000万+的真实住宅IP池,日本节点支持城市级定位,会话时长和轮换策略都能自定义,HTTP/HTTPS/SOCKS5协议全兼容,按流量计费对中小规模任务来说成本很友好。如果你需要更大规模、更高并发的场景,他们家还有动态不限量的方案,100Gbps+带宽,不限流量和IP调用次数,高并发长时任务跑起来成本优势很明显。另外也支持指定国家/地区、IP规模、并发能力的专属定制,有特殊需求可以直接跟他们的技术团队对接。再次提醒,网帆代理的海外代理套餐仅适用于中国大陆以外地区,大陆网络环境无法直接使用。
以上就是这次日本动态IP代理爬虫的完整记录。没有什么高深的技术,就是把选池、配参数、写代码、调稳定性这几个环节一个一个磨到位。希望对你有用。
注意:海外代理套餐仅适用于中国大陆以外地区,大陆网络环境无法直接使用。
