如何用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库读取文件的魔法数来确定真实格式:
- 先装库:
# Windows用这个 pip install python-magic-bin # Linux/macOS用这个 pip install python-magic
- 修改对应的代码部分:
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
相关产品推荐
相关产品推荐

