记录一次Crawl遇到的301重定向问题

本文介绍如何通过设置requests.get()的allow_redirects参数来处理网页301永久重定向的问题,确保能够正常获取页面内容,并建议设置timeout避免IP被封。

在浏览器中打开网页正常运行,模拟浏览器运行就会返回301永久重定向,这是网站设置的一种反PA机制,那么我们在requests.get()中设置allow_redirects=False,将允许重定向设置为FALSE,

res = requests.get(url=s, headers=self.headers2, allow_redirects=False, timeout=50).headers['Location']

我们可以获取重定向返回的地址,再次发起一次请求,就可以返回成功

requests.get(url=wen, headers=self.headers2, allow_redirects=False, timeout=50).text

我们打印返回结果就可以发现可以正常返回,为了被封IP,建议设置timeout。

 

<think> 我们遇到的问题是:在爬取第一个网页后,从中提取了第二个网页的URL,但用这个URL去请求时返回404错误。然而,如果我们用另一个代码(可能是直接复制该URL到浏览器或单独请求)却可以成功获取第二个网页。 可能的原因及解决方案: 1. **相对路径与绝对路径问题** 第一个网页中提取的URL可能是相对路径,需要拼接成绝对路径。 例如:提取的URL是`/chapter/123.html`,而实际需要`https://www.example.com/chapter/123.html`。 2. **HTTP头部问题(User-Agent, Referer, Cookie等)** 第二个网页可能对请求头有要求,比如检查`Referer`(来源页面)是否为第一个网页,或者需要特定的`User-Agent`,甚至需要登录后的`Cookie`。 3. **动态生成内容** 第二个网页的内容可能是通过JavaScript动态加载的,直接请求获取的HTML并没有包含有效内容(此时需要模拟浏览器行为,如使用Selenium)。 4. **反爬机制** 网站可能有反爬措施,例如检测到爬虫行为(短时间内频繁请求)而拒绝服务。需要添加延迟、使用代理或更换User-Agent。 5. **会话(Session)维持** 如果网站需要登录,那么第二个网页可能需要保持登录状态(使用同一个Session对象发送请求)。 6. **URL编码问题** 提取的URL可能包含特殊字符(如空格、中文等),需要正确编码。 7. **时间戳或随机参数** 某些URL会带有时间戳或随机参数(如`?t=123456`),如果第一个网页生成的URL带有这样的参数,而我们在请求第二个网页时没有携带,可能导致404。 根据问题描述,另一个代码能直接获取第二个网页,说明问题很可能出现在我们构建请求的方式上(比如头部信息或会话状态)。 让我们逐步检查: **步骤1:检查URL是否正确** 打印出从第一个网页提取的URL,然后手动在浏览器中访问这个URL,看是否正常。如果手动访问也不正常,说明提取的URL有问题(可能是解析错误)。 如果手动访问正常,但程序访问不正常,则可能是程序的问题(如请求头、会话等)。 **步骤2:确保使用绝对URL** 使用`urllib.parse.urljoin(base_url, relative_url)`将相对URL转换为绝对URL。 例如: ```python from urllib.parse import urljoin base_url = "https://www.example.com" relative_url = "/chapter/1.html" absolute_url = urljoin(base_url, relative_url) # 得到 "https://www.example.com/chapter/1.html" ``` **步骤3:检查请求头** 第二个网页可能需要特定的请求头。特别是`Referer`,它告诉服务器当前请求是从哪个页面链接过来的。 我们可以复制第一个网页的URL作为`Referer`。 例如: ```python headers = { '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', 'Referer': first_page_url # 第一个网页的URL } ``` **步骤4:使用同一个Session** 如果网站需要维持会话(比如登录状态),则应该使用`requests.Session()`来保持Cookie。 例如: ```python with requests.Session() as s: # 先请求第一个网页 r1 = s.get(first_page_url, headers=headers) # 从r1中解析出第二个网页的URL next_url = parse_next_url(r1.text) # 用同一个Session请求第二个网页,会自动携带Cookie r2 = s.get(next_url, headers=headers) ``` **步骤5:模拟浏览器行为(针对动态内容)** 如果第二个网页是动态加载的,我们需要使用Selenium或类似工具。 但根据描述,另一个代码能直接获取,说明可能是静态页面,所以这一步可以先不考虑。 **步骤6:添加延迟** 在请求之间添加随机延迟,避免触发反爬。 例如:`time.sleep(random.uniform(1, 3))` **步骤7:检查重定向** 有些网页会重定向,我们可以设置`allow_redirects=True`(默认就是True)并检查最终URL。 例如: ```python response = requests.get(url, headers=headers, allow_redirects=True) print(response.url) # 打印最终重定向后的URL ``` **步骤8:日志记录** 记录每次请求的URL、状态码、响应内容(或部分内容)以及请求头,方便调试。 下面是一个综合示例代码,包含上述多个步骤: ```python import requests from urllib.parse import urljoin import time import random from bs4 import BeautifulSoup # 基础URL base_url = "https://www.example.com" # 第一个网页的URL start_url = "https://www.example.com/book/123.html" # 自定义请求头 headers = { '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', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', } def get_next_url(html): """从第一个网页的HTML中解析出下一个URL(这里假设是一个链接)""" soup = BeautifulSoup(html, 'html.parser') # 假设下一个链接在id为"next"的a标签中 next_tag = soup.find('a', id='next') if next_tag: relative_url = next_tag.get('href') # 转换成绝对URL next_url = urljoin(base_url, relative_url) return next_url return None # 使用Session保持会话 with requests.Session() as s: # 第一次请求 print(f"请求第一个网页: {start_url}") r1 = s.get(start_url, headers=headers) r1.raise_for_status() # 如果状态不是200,抛出异常 # 设置编码(如果网页有指定编码,可以不用设置) r1.encoding = r1.apparent_encoding # 解析出下一个URL next_url = get_next_url(r1.text) if not next_url: print("没有找到下一个URL") exit() print(f"解析出的下一个URL: {next_url}") # 添加随机延迟(1~3秒) delay = random.uniform(1, 3) print(f"等待{delay:.2f}秒...") time.sleep(delay) # 修改请求头,添加Referer(来源页) headers['Referer'] = start_url # 第二次请求 print(f"请求第二个网页: {next_url}") r2 = s.get(next_url, headers=headers) # 输出状态码 print(f"状态码: {r2.status_code}") # 如果状态码是404,说明请求失败 if r2.status_code == 404: print("请求第二个网页失败,返回404") # 打印响应内容的前500字符(可能包含错误信息) print(r2.text[:500]) else: r2.raise_for_status() # 处理第二个网页的内容... print("请求成功!") ``` 如果上述方法仍然不能解决,可以尝试以下高级调试: - 使用网络抓包工具(如Charles或Fiddler)查看浏览器请求和程序请求的差异(特别是请求头、请求参数)。 - 检查是否有JavaScript重定向(虽然返回404,但浏览器执行了JS跳转,而程序没有)。 希望这些方法能帮助你解决问题! §§相关问题§§ 1. 如何用Python处理网页中的相对URL? 2. 如何设置请求头(如User-Agent、Referer)以避免被网站屏蔽? 3. Requests库中Session对象有什么作用? 4. 如何用Python爬虫处理动态加载的网页内容? 5. 常见的HTTP状态码(如404、403)分别代表什么?遇到时如何解决? </think> 针对 Python 爬虫获取第一个网页后,根据提取的 URL 请求第二个网页返回 404,但直接访问该 URL 却能成功的问题,以下是系统性解决方案: ### 问题根因分析 1. **URL 拼接错误** 从第一个网页提取的 URL 可能是相对路径,未正确拼接为绝对路径[^1][^3] 2. **请求头缺失关键字段** 缺少 `Referer` 或 `Origin` 头部,触发网站反爬机制[^3][^4] ```python # 错误示例 - 缺少关键头部 requests.get(url) ``` 3. **会话状态未保持** 未使用 Session 对象,导致登录状态/Cookie 丢失[^4] ```python # 错误示例 - 未维持会话 r1 = requests.get(url1) r2 = requests.get(url2) # 会话断开 ``` 4. **动态参数缺失** 部分网站 URL 包含时间戳或动态 token,未正确提取[^2][^5] --- ### 解决方案 #### 1. URL 规范处理 使用 `urllib.parse.urljoin` 确保绝对路径: ```python from urllib.parse import urljoin # 从第一个页面提取相对路径 next_path = soup.select_one('.next-page')['href'] # 转换为绝对 URL absolute_url = urljoin("https://www.wodeshucheng.net", next_path) ``` #### 2. 完整请求头配置 添加必要头部防止反爬: ```python headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Referer': first_page_url, # 关键!标明来源页面 'Accept-Language': 'zh-CN,zh;q=0.9', 'Accept-Encoding': 'gzip, deflate', } response = requests.get(absolute_url, headers=headers) ``` #### 3. 会话状态保持 使用 Session 对象维持 Cookie: ```python with requests.Session() as s: # 首次请求 r1 = s.get(first_url, headers=headers) # 提取下一页URL next_url = parse_next_url(r1.text) # 保持会话状态的请求 r2 = s.get(next_url, headers=headers) # 自动携带Cookie ``` #### 4. 动态参数处理 若 URL 包含动态参数(如 `?_t=1689293832`): ```python # 从页面中提取完整链接而非手动拼接 next_url = soup.select_one('a.next')['href'] # 或从JS代码中提取动态参数 import re script = soup.find('script', string=re.compile('nextUrl')) next_url = re.search(r'var nextUrl = "(.*?)"', script.string).group(1) ``` #### 5. 调试建议 添加响应追踪: ```python print(f"请求URL: {response.url}") print(f"状态码: {response.status_code}") print(f"响应头: {response.headers}") print(f"重定向链: {response.history}") # 检查重定向 ``` --- ### 完整工作流程 ```python import requests from bs4 import BeautifulSoup from urllib.parse import urljoin def crawl_series(start_url): session = requests.Session() headers = {'User-Agent': 'Mozilla/5.0', 'Referer': start_url} current_url = start_url while current_url: # 请求当前页 response = session.get(current_url, headers=headers) soup = BeautifulSoup(response.text, 'html.parser') # 处理当前页内容... save_content(soup) # 获取下一页(优先选择JS生成的链接) next_tag = soup.select_one('script:contains("nextChapter")') if next_tag: next_url = extract_js_url(next_tag.string) # 自定义JS解析函数 else: next_rel = soup.select_one('a.next-page')['href'] next_url = urljoin(current_url, next_rel) # 更新Referer headers['Referer'] = current_url current_url = next_url # 示例调用 crawl_series("https://www.wodeshucheng.net/book/123.html") ``` > **关键点**:网站通常通过验证 `Referer` 和会话连续性来防御爬虫。使用 Session + 动态更新 Referer 可解决 95% 的 404 问题[^3][^4]。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值