Python爬虫实现美剧资源批量抓取与保存

叶初
叶初
发布时间:2026-10-12| 2人浏览过
原创

本文介绍如何使用Python编写爬虫程序,自动抓取美剧网站上的所有剧集下载链接。通过遍历URL编号、利用status_code过滤无效页面,并结合正则表达式提取ed2k链接,最终将结果按剧名保存为txt文件。文章还探讨了文件命名中的特殊字符处理及多线程在Python GIL限制下的实际效果。

保持观看美剧的习惯,既能锻炼英语听力,也能打发闲暇时光。此前,视频网站还能提供在线播放服务,但受广电总局相关管理规定影响,进口的美剧和英剧不再像过去那样同步更新。然而,对于不愿追剧的我来说,这并非不可克服的障碍。经过网络搜索,我发现了一个支持迅雷下载的美剧资源站【天天美剧】,上面提供了丰富的下载资源。近期,我还迷上了BBC制作的高清自然纪录片,画面精美,令人赏心悦目。

虽然找到了资源网站,但每次下载都需要手动打开浏览器、输入网址、寻找特定剧集并点击下载链接。这一过程繁琐且耗时,加之网站链接偶尔无法访问,体验并不理想。鉴于我一直在学习Python爬虫技术,于是决定动手编写一个爬虫程序,旨在抓取该网站上所有的美剧链接,并将其保存至文本文档中。这样,用户只需打开文档,复制所需的链接到迅雷即可进行下载,极大简化了操作流程。

起初,我计划采用一种递归式的爬取策略:从主页出发,发现一个URL便使用requests库打开并抓取下载链接,试图覆盖全站。然而,该网站存在大量重复链接,且其URL结构并不像我预期的那样规则。经过多次尝试,我未能构建出理想的发散式爬虫逻辑,或许是因为技术积累尚浅。尽管如此,这仍是一个继续学习和努力的过程。

随后,我注意到电视剧的下载链接均位于文章内部,而文章的URL末尾包含一个数字编号,例如http://cn163.net/archives/24016/。基于这一发现,我借鉴了之前的爬虫经验,采取了一种更高效的解决方案:通过自动生成URL来遍历网站。既然URL末尾的数字是变化的,且每部剧对应唯一的编号,我便尝试估算文章的大致数量,并利用Python的range函数连续生成数字,从而构造出完整的URL序列。

在遍历过程中,许多URL实际上并不存在,直接访问会导致程序报错。对此,我们利用requests库自带的status_code属性来判断请求返回的状态。对于返回状态码为404的URL,我们直接跳过;而对于其他状态的URL,则进入页面进行链接爬取。这一机制有效解决了无效URL导致程序中断的问题。

以下是上述步骤的具体实现代码。

def get_urls(self):
  try:
    for i in range(2015,25000):
      base_url='http://cn163.net/archives/'
      url=base_url+str(i)+'/'
      if requests.get(url).status_code == 404:
        continue
      else:
        self.save_links(url)
  except Exception,e:
    pass

后续流程进展较为顺利。我在网上参考了前人编写的类似爬虫,但那些代码仅能爬取单篇文章,因此我借鉴了其正则表达式部分。虽然我也尝试使用BeautifulSoup库,但发现其效果不如正则表达式理想,故果断放弃。此外,当前抓取效果并非完美,约有半数左右的链接未能正确抓取,后续仍需进一步优化算法。

# -*- coding:utf-8 -*-
import requests 
import re
import sys
import threading
import time
reload(sys)
sys.setdefaultencoding('utf-8')
class Archives(object):

  def save_links(self,url):
    try:

      data=requests.get(url,timeout=3)
      content=data.text
      link_pat='"(ed2k://\|file\|[^"]+?\.(S\d+)(E\d+)[^"]+?1024X\d{3}[^"]+?)"'
      name_pat=re.compile(r'<h2 class="entry_title">(.*?)</h2>',re.S)
      links = set(re.findall(link_pat,content))
      name=re.findall(name_pat,content)
      links_dict = {}
      count=len(links)
    except Exception,e:
      pass
    for i in links:
      links_dict[int(i[1][1:3]) * 100 + int(i[2][1:3])] = i#把剧集按s和e提取编号
    try:
      with open(name[0].replace('/',' ')+'.txt','w') as f:
        print name[0]
        for i in sorted(list(links_dict.keys())):#按季数+集数排序顺序写入
          f.write(links_dict[i][0] + '\n')
      print "Get links ... ", name[0], count
    except Exception,e:
      pass

  def get_urls(self):
    try:
      for i in range(2015,25000):
        base_url='http://cn163.net/archives/'
        url=base_url+str(i)+'/'
        if requests.get(url).status_code == 404:
          continue
        else:
          self.save_links(url)
    except Exception,e:
      pass
  def main(self):
    thread1=threading.Thread(target=self.get_urls())
    thread1.start()
    thread1.join()
  if __name__ == '__main__':
  start=time.time()
  a=Archives()
  a.main()
  end=time.time()
  print end-start

完整代码中使用了多线程技术,但考虑到Python的全局解释器锁(GIL)机制,多线程的实际加速效果有限。尽管网站显示有两万多部剧集,本以为抓取过程会耗时良久,但排除掉无效URL和未匹配到的内容后,总抓取时间不到20分钟。原本我还考虑使用Redis在两台Linux服务器上分布式爬取,但经过尝试后发现并无必要,因此决定维持当前方案。若未来需要处理更大数据量,再考虑扩展架构。

在开发过程中,我还遇到了一个令人头疼的问题:文件名的保存规则。特此吐槽一下,txt文本格式的文件名允许包含空格,但严禁包含斜线、反斜线、括号等特殊字符。正是由于这个限制,我花费了大量时间排查。起初我误以为是数据抓取错误,经过仔细检查才发现,问题根源在于爬取的剧名中包含了斜杠符号,这给文件保存带来了巨大困扰。

本文作者:码农网 – 肖豪

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。