You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Beautiful Soup下载带原始扩展名的不同格式文件

问题

我用Python写了个网络爬虫,读取URL列表后下载每个页面里的文件。但保存的文件都没扩展名(虽然能正常打开),涉及pdf、rtf、docx这几种格式。我得让文件保存时带上原始扩展名,方便后续处理,最终要把每个文件转成txt格式。

原代码:

import requests
from bs4 import BeautifulSoup
import urllib.request
import pandas as pd
import os
import textract
import glob

#list of urls to visit
commission_links = pd.read_csv(list_of_urls)
url_list = commission_links['Url'].tolist()


for url in url_list:
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    link_div = soup.find('div', {'class': 'pull-right v-img dropdown icone-info bgc-comissao'})
    file_link = link_div.find('a')['href']
    file_response = requests.get(file_link, stream = True)

    #extract the file type from the file_link using split and indexing
    filename = os.path.basename(file_link)
    extension = os.path.splitext(filename)[1]
    print(extension)

    with open(f"file_{filename}{extension}", "wb") as f:
       f.write(file_response.content)
       print(f"File saved: file_{filename}{extension}")

测试用URL:

commission_links = ['https://legis.senado.leg.br/comissoes/reuniao?reuniao=10944&codcol=50',
                    'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10892&codcol=50',
                    'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10891&codcol=50',
                    'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10866&codcol=50',
                    'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10844&codcol=50']
解决办法

你的代码拿不到扩展名,核心原因是目标文件的URL本身就没带后缀,用os.path.splitext自然提取不出来。可以试下面两种方法:

方法1:从响应头Content-Type拿扩展名

服务器返回文件时,一般会在响应头的Content-Type里标注文件类型,我们可以提前做个映射表来转成扩展名:

import requests
from bs4 import BeautifulSoup
import pandas as pd
import os
import textract
import glob

# 建立Content-Type到扩展名的对应关系
content_type_map = {
    'application/pdf': '.pdf',
    'application/rtf': '.rtf',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',
    # 可以根据需要加更多类型
}

# 测试用URL(实际用的话替换成读取CSV的代码)
url_list = ['https://legis.senado.leg.br/comissoes/reuniao?reuniao=10944&codcol=50',
            'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10892&codcol=50',
            'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10891&codcol=50',
            'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10866&codcol=50',
            'https://legis.senado.leg.br/comissoes/reuniao?reuniao=10844&codcol=50']

for idx, url in enumerate(url_list):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    link_div = soup.find('div', {'class': 'pull-right v-img dropdown icone-info bgc-comissao'})
    
    if not link_div:
        print(f"没找到文件链接: {url}")
        continue
    
    file_link = link_div.find('a')['href']
    # 处理相对链接,补全域名
    if not file_link.startswith('http'):
        file_link = 'https://legis.senado.leg.br' + file_link
    
    file_response = requests.get(file_link, stream=True)
    # 检查请求是否成功,失败就跳过
    try:
        file_response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        print(f"下载文件失败: {url}, 错误: {e}")
        continue

    # 从Content-Type提取扩展名
    content_type = file_response.headers.get('Content-Type', '').split(';')[0].strip()
    extension = content_type_map.get(content_type, '')

    # 如果响应头没拿到,试试从链接文本里找关键词
    if not extension:
        link_text = link_div.find('a').get_text(strip=True)
        if 'PDF' in link_text:
            extension = '.pdf'
        elif 'DOCX' in link_text:
            extension = '.docx'
        elif 'RTF' in link_text:
            extension = '.rtf'

    # 用索引做文件名,避免重复和非法字符
    filename = f"file_{idx}{extension}"
    # 分块写入大文件更稳定
    with open(filename, "wb") as f:
        for chunk in file_response.iter_content(chunk_size=8192):
            f.write(chunk)
    print(f"文件已保存: {filename}")

    # 转成txt格式(要确保textract的依赖都装了,比如pdf需要poppler,docx需要python-docx等)
    try:
        text_content = textract.process(filename)
        txt_filename = os.path.splitext(filename)[0] + '.txt'
        with open(txt_filename, 'wb') as txt_f:
            txt_f.write(text_content)
        print(f"已转成TXT: {txt_filename}")
    except Exception as e:
        print(f"{filename}转TXT失败: {str(e)}")

方法2:用文件魔法数检测真实类型

如果响应头不可靠,可以用python-magic库读取文件的魔法数来确定真实格式:

  1. 先装库:
# Windows用这个
pip install python-magic-bin
# Linux/macOS用这个
pip install python-magic
  1. 修改对应的代码部分:
import magic

# ... 其他代码不变 ...

file_response = requests.get(file_link, stream=True)
file_response.raise_for_status()

# 先把内容读到内存,用魔法数检测
content = file_response.content
mime_type = magic.from_buffer(content, mime=True)
extension = content_type_map.get(mime_type, '')

# 后续保存和转换逻辑和方法1一样
注意点
  • 处理相对链接:页面里的文件链接可能是相对路径,必须补全成完整URL才能下载。
  • 异常处理:加了链接不存在、下载失败、转换失败的判断,避免程序直接崩掉。
  • 文件名唯一性:用循环索引当文件名的一部分,避免因为原URL的问题导致文件名重复或包含非法字符。

内容的提问来源于stack exchange,提问作者Tony C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:32:04