ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python如何通过URL链接判断文件类型

2026/8/6 2:52:49 拓冰建站 浏览量
Python如何通过URL链接判断文件类型

在爬虫开发中经常遇到一类场景:拿到一批URL,有的是普通HTML网页需要解析文本,有的是PDF、Excel、压缩包等下载文件需要保存二进制。
仅仅依靠URL后缀判断并不靠谱,很多动态下载接口没有真实文件后缀;直接全部请求完整内容又浪费带宽。本文分享一套兼顾性能与准确度的实现方案。

业务场景

爬虫抓取列表页拿到大量链接,我们需要做分流:

  1. 如果是HTML网页:请求页面,提取网页文本内容;
  2. 如果是文件资源(PDF/Word/Excel/ZIP等):直接下载保存二进制;
  3. 识别失败:标记为unknown,做兜底处理。
    同时文件类型字符串可以直接存入数据库,方便后续业务使用。

判断思路总览

一共有两种判断手段,组合使用兼顾性能与准确率:

1. URL后缀本地快速判断(无网络请求)

解析URL路径,提取末尾文件扩展名。
优点:零网络开销,速度极快
缺点:不可靠。动态下载接口/download?id=123没有后缀;也存在伪装后缀,例如xxx.pdf实际返回HTML页面。

适合做前置过滤,能识别就直接返回,减少不必要的HTTP请求。

2. HTTP响应头探测(权威判断)

优先发送HEAD请求,只获取响应头,不下载响应体

  • Content-Type:资源MIME类型,用来区分是text/html网页还是application/pdf等二进制文件;
  • Content‑Disposition:如果头部包含attachment,代表浏览器触发下载,里面还可以拿到服务器下发的真实文件名,这对无后缀动态下载接口最为关键。

部分服务器拒绝HEAD请求,此时降级为GET + stream=True,依然只读取响应头,不会拉取完整文件内容。

优先级顺序:
Content‑Disposition中的真实文件名后缀>MIME类型映射>URL路径后缀兜底

完整可运行代码

importrequestsimportrefromurllib.parseimporturlparse,unquote# 需要识别的下载后缀集合(带点小写)DOWNLOAD_EXT={".pdf",".zip",".rar",".7z",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".txt",".csv",".jpg",".jpeg",".png",".gif",".bmp",".mp4",".mp3",".tar",".gz"}# 网页后缀集合HTML_EXT={".html",".htm",".php",".asp",".aspx",".jsp"}# MIME类型映射为文件后缀MIME_MAP={"application/pdf":"pdf","application/zip":"zip","application/x-zip-compressed":"zip","application/vnd.openxmlformats-officedocument.wordprocessingml.document":"docx","application/msword":"doc","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"xlsx","application/vnd.ms-excel":"xls","text/csv":"csv","image/jpeg":"jpg","image/png":"png","text/plain":"txt"}HEADERS={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"}defcheck_resource_type(url:str,timeout=8)->str:""" 判断URL资源类型 :param url: 待检测链接 :param timeout: 请求超时时间 :return: "html" | "pdf"/"docx"/"zip"... | "unknown" """# 第一步:本地解析URL后缀快速判断,不走网络parse_result=urlparse(url)path=parse_result.path.lower()dot_index=path.rfind(".")ifdot_index!=-1:ext_with_dot=path[dot_index:]ext_raw=path[dot_index+1:]ifext_with_dotinHTML_EXT:return"html"ifext_with_dotinDOWNLOAD_EXT:returnext_raw# 本地无法判断,发起HTTP头探测try:resp=requests.head(url,headers=HEADERS,timeout=timeout,allow_redirects=True)exceptException:# HEAD被拒绝,降级GET,stream=True不下载bodytry:resp=requests.get(url,headers=HEADERS,timeout=timeout,allow_redirects=True,stream=True)exceptException:return"unknown"content_type=resp.headers.get("Content-Type","").lower().split(";")[0].strip()disposition=resp.headers.get("Content-Disposition","")# 判断是否HTML网页if"text/html"incontent_type:return"html"# 优先从Content‑Disposition提取服务器返回的文件名后缀(动态下载接口核心)fn_match=re.search(r'filename\*?=(?:["\']?)([^"\';\n]+)',disposition)iffn_match:filename=fn_match.group(1)# 处理RFC5987编码文件名 filename*=utf-8''xxx.pdfiffilename.startswith("utf-8''"):filename=unquote(filename[7:])filename=filename.lower()fd=filename.rfind(".")iffd!=-1:returnfilename[fd+1:]# MIME映射获取后缀ifcontent_typeinMIME_MAP:returnMIME_MAP[content_type]# 兜底再次使用URL后缀ifdot_index!=-1:returnpath[dot_index+1:]return"unknown"if__name__=="__main__":test_urls=["https://example.com/detail.html","https://example.com/report.pdf","https://example.com/api/download?id=100","https://example.com/no_suffix_page"]fortest_urlintest_urls:t=check_resource_type(test_url)print(f"{test_url}-->{t}")

业务调用示例

返回的字符串可以直接存入数据库varchar字段。

url="https://xxx.com/xxx"res_type=check_resource_type(url)ifres_type=="html":# 网页,请求获取文本passelse:# 文件,res_type为后缀,unknown时兜底为binsuffix=res_typeifres_type!="unknown"else"bin"save_filename=f"output.{suffix}"# 执行文件保存逻辑

返回值行为示例

URL示例返回结果业务动作
xxx/detail.htmlhtml解析网页文本
xxx/file.pdfpdf保存pdf文件
xxx/api/download?id=123,响应头携带attachment;filename="data.xlsx"xlsx保存xlsx文件
无后缀链接返回HTML页面html解析网页文本
二进制资源全部识别失败unknown保存为.bin

注意坑点

  1. 必须开启allow_redirects=True:很多下载链接会302重定向到真实资源地址,不跟随重定向拿到的是错误头信息。
  2. HEAD请求会被部分服务器405拒绝,代码已经做降级GET+stream=True,不会下载完整body,只会读取headers。
  3. 反爬:请求一定要带上User‑Agent,否则部分网站直接返回403。
  4. 网络异常:超时、连接失败直接返回unknown,不会抛出异常中断爬虫主流程。
  5. 局限性:极少数网站内容类型错误配置,会造成误判;生产环境建议做好异常捕获与日志记录。

性能优化提示

大量URL循环调用的时候,大部分普通HTML页面会在本地URL后缀阶段直接返回,不会产生网络请求
只有没有后缀的模糊链接,才会发送HTTP探测请求,大幅减少爬虫网络开销。

完整函数对外只有一个入口,返回简单字符串,方便数据库存储与后续分流处理。