使用BeautifulSoup爬取Xcel Energy网站xls文件时遭遇urllib 404错误
解决爬取Xcel Energy官网xls表格时的404错误
嘿,我之前爬这类企业官网的时候也碰到过一模一样的404问题,大概率是这几个常见原因,咱们一步步来排查解决:
1. 先确认你构造的URL是不是完整路径
很多时候页面里的下载链接是相对路径(比如/documents/xxx.xls),如果你直接把这个相对路径当成完整URL去请求,服务器肯定找不到资源返回404。
解决方法是用urllib.parse.urljoin把主域名和相对路径拼接成完整URL:
from urllib.parse import urljoin from bs4 import BeautifulSoup import urllib.request base_url = "https://www.xcelenergy.com" page_url = f"{base_url}/working_with_us/municipalities/community_energy_reports" # 获取页面内容(先加个简单的UA头避免被拦截) req = urllib.request.Request(page_url, headers={"User-Agent": "Mozilla/5.0"}) page_content = urllib.request.urlopen(req).read() soup = BeautifulSoup(page_content, "html.parser") # 提取xls链接并拼接完整URL xls_links = [] for a_tag in soup.find_all("a", href=lambda x: x and x.endswith(".xls")): relative_url = a_tag["href"] full_url = urljoin(base_url, relative_url) xls_links.append(full_url) print("完整下载URL:", full_url)
拼接完之后,一定要手动把这个URL复制到浏览器里打开——如果浏览器也打不开,那可能是链接本身失效了;如果浏览器能打开,那就是请求头或者反爬的问题。
2. 给请求添加浏览器伪装的请求头
urllib默认的请求头是Python-urllib/3.x这种标识,很多网站会直接拦截这类非浏览器的请求,返回404(其实是服务器拒绝访问,伪装成404)。
你需要给请求添加User-Agent等头信息,伪装成浏览器访问:
import urllib.request headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Referer": base_url # 有些网站会检查Referer,确保请求来自官网页面 } for full_url in xls_links: try: req = urllib.request.Request(full_url, headers=headers) response = urllib.request.urlopen(req) # 保存文件 filename = full_url.split("/")[-1] with open(filename, "wb") as f: f.write(response.read()) print(f"成功下载: {filename}") except urllib.error.HTTPError as e: print(f"下载失败 {full_url}: {e}")
如果觉得urllib写起来麻烦,推荐用requests库,代码更简洁直观:
import requests headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"} for full_url in xls_links: try: response = requests.get(full_url, headers=headers) response.raise_for_status() # 主动抛出HTTP错误 filename = full_url.split("/")[-1] with open(filename, "wb") as f: f.write(response.content) print(f"成功下载: {filename}") except requests.exceptions.RequestException as e: print(f"下载失败 {full_url}: {e}")
3. 检查页面是否是动态加载内容
如果上面两步都试过还是404,那可能页面的下载链接是通过JavaScript动态渲染出来的——BeautifulSoup只能解析静态HTML,抓不到JS生成的链接。
这种情况可以用Selenium模拟浏览器加载页面,然后提取真实链接:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options import requests # 配置无头浏览器(不打开可视化窗口) chrome_options = Options() chrome_options.add_argument("--headless=new") 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/118.0.0.0 Safari/537.36") driver = webdriver.Chrome(options=chrome_options) driver.get("https://www.xcelenergy.com/working_with_us/municipalities/community_energy_reports") driver.implicitly_wait(5) # 等待页面元素加载 # 提取所有xls链接 xls_links = [] for a_tag in driver.find_elements(By.XPATH, "//a[contains(@href, '.xls')]"): full_url = a_tag.get_attribute("href") if full_url not in xls_links: xls_links.append(full_url) # 下载文件 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"} for full_url in xls_links: try: response = requests.get(full_url, headers=headers) response.raise_for_status() filename = full_url.split("/")[-1] with open(filename, "wb") as f: f.write(response.content) print(f"成功下载: {filename}") except requests.exceptions.RequestException as e: print(f"下载失败 {full_url}: {e}") driver.quit()
最后排查小技巧
- 用浏览器的开发者工具(F12)查看网络请求:找到下载xls的请求,复制它的完整URL和请求头,和你脚本里的对比,就能快速定位问题。
- 有些网站会对频繁请求的IP进行封禁,如果多次请求失败,可以尝试添加延迟(比如
time.sleep(2))或者使用代理IP。
内容的提问来源于stack exchange,提问作者deutschnozzle
相关产品推荐
相关产品推荐

