爬取2024新番(复习python爬虫)

自从去年10月份,我就再没写过类似编程类的文章了,python也差不多忘了

(记得22年底学会的爬虫,现在忘干净了都)不过爬虫确实是一个伟大的发明啊, 解放了多少懒人的双手

所以,今天写个爬虫练练手吧,就爬取bangumi的2024动画列表吧

研究bangumi网页特点

我个人认为,要用爬虫爬取不同网页,程序必须是类似因地制宜的。打个比方,有的网站图片习惯用img标签展示,有点则另辟蹊径,用div展示;想爬取图片只能get div标签。

正因此,不能把程序写死了,一个爬虫程序仅针对一个网页结构,各个网站结构都不同,所以在写代码前做好调查是必不可少的。

以bangumi.tv为例

我们想要爬取的是24年所有番剧的名称(用红框框框的蓝色文字)

在控制台中,我们惊奇的发现:它是在a标签中的!

<h3>里面套<a>,后边要用

开干

这就好办了,已知url为https://bangumi.tv/anime/browser/airtime/2024,获取元素位置也知道了,即可得到以下代码(辛辛苦苦加了注释,自己看吧):

# 导入库
import requests
from bs4 import BeautifulSoup

# 请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/78.0.3904.108 Safari/537.36 '
}


# 获取网页
url = 'https://bangumi.tv/anime/browser/airtime/2024'
print(url)
response = requests.get(url, headers=headers)  # 请求地址
soup = BeautifulSoup(response.text, 'lxml')  # BeautifulSoup处理

# 提取链接
list = []
for img in soup.find_all('h3'):  # 遍历寻找h3标签
    list.append(img.find('a').text)  #从刚刚获取的h3标签中找a标签
print(list)
# 注:因为网页的结构就是h3标签套a标签,类似父子标签,所以有以上操作

运行之后,惊奇的发现——

全~是~乱码

easy,忘加编码了,在请求(requests.get)后边加个编码

response.encoding = 'utf-8'
可以正常显示了

迎来了第二个问题:bangumi它是分页展示的,这就是说一个页面无法完全展示所有的番

咋办呢?

我之前说过嘛,要因地制宜,我们又惊奇的发现——

每一页都会在地址后方加上?page=页数

这个我会!遍历嘛,每次遍历让地址末尾+1,页数也就+1了嘛(遍历次数即为页数,想获取多少也就遍历多少次)

然后每次遍历都请求一次,将番名存放至列表中

(又是辛辛苦苦的加的注释…累)

# 导入库
import requests
from bs4 import BeautifulSoup

# 请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/78.0.3904.108 Safari/537.36 '
}


# 获取网页
for n in range(3):  # 遍历3次
    n += 1
    # 获取网页
    url = 'https://bangumi.tv/anime/browser/airtime/2024?page=' + str(n)  # 每次page+1
    print(url)
    response = requests.get(url, headers=headers)  # 请求地址
    response.encoding = 'utf-8'  # 编码改为utf-8
    soup = BeautifulSoup(response.text, 'lxml')  # BeautifulSoup处理

# 提取链接
list = []
for img in soup.find_all('h3'):  # 遍历寻找h3标签
    list.append(img.find('a').text)  #从刚刚获取的h3标签中找a标签
print(list)
# 注:因为网页的结构就是h3标签套a标签,类似父子标签,所以有以上操作

非常滴好!获取了第1、2、3页


但又出现了一个致命bug:每次遍历都会清空soup变量,导致只能把最后一页的内容给列表,前两页的都没了

咋办呢?

让它每次遍历都加到列表里呗

(不加注释了,太懒了)

import requests
from bs4 import BeautifulSoup

# 请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/78.0.3904.108 Safari/537.36 '
}

list = []  # 储存列表
for n in range(3):
    n += 1
    # 获取网页
    url = 'https://bangumi.tv/anime/browser/airtime/2024?page=' + str(n)
    print(url)
    response = requests.get(url, headers=headers)
    response.encoding = 'utf-8'
    soup = BeautifulSoup(response.text, 'lxml')

    for img in soup.find_all('h3'):  # 遍历获取标题
        list.append(img.find('a').text)   # 存到列表里
print(list)


但是总不能在控制台读吧,还是把它放到txt文件里吧

import requests
from bs4 import BeautifulSoup

# 请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/78.0.3904.108 Safari/537.36 '
}

list = []  # 储存列表
for n in range(21):
    n += 1
    # 获取网页
    url = 'https://bangumi.tv/anime/browser/airtime/2024?page=' + str(n)
    print(url)
    response = requests.get(url, headers=headers)
    response.encoding = 'utf-8'
    soup = BeautifulSoup(response.text, 'lxml')

    for img in soup.find_all('h3'):  # 遍历获取标题
        list.append(img.find('a').text)
print(list)

# 写入txt
f = open('1.txt', 'w', encoding='utf-8', errors='ignore')  # 别忘了encoding编码,要不又乱码了...
list = "\n".join(list)  # 分词,每行一个
f.write(list)
f.close()

我看我这儿bangumi有21页,我遍历了21次

那咱的目的就达成了

改进

我又想了很多,这个程序还有很多的改进地方,比如多次递归来获取评分或发布日期,然后根据她们排序

当然,也可以通过深度优先搜索、广度优先搜索来查找番剧。不过爬虫这个东西就是因地制宜来制作程序,再怎么说也脱离不了网页的结构。

总之,这篇文章就到这里了。不过今年四月新番真不错啊!

评论

  1. 匿名
    Windows Edge
    15 小时前
    2025-1-21 17:38:24

    大佬在你基础上让gpt帮改进了一下代码

    import os
    import time
    import random
    import requests
    from bs4 import BeautifulSoup
    
    # 请求头
    HEADERS = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/78.0.3904.108 Safari/537.36'
    }
    
    # 替换非法字符的映射表
    ILLEGAL_CHAR_MAP = {
        '<': '<',
        '>': '>',
        ':': ':',
        '"': '"',
        '/': '/',
        '\\': '\',
        '|': '|',
        '?': '?',
        '*': '*'
    }
    
    
    def sanitize_filename(name):
        """
        替换文件名中的非法字符
        :param name: 原始文件名
        :return: 清理后的文件名
        """
        for illegal_char, replacement in ILLEGAL_CHAR_MAP.items():
            name = name.replace(illegal_char, replacement)
        return name
    
    
    def get_total_pages(year):
        """
        获取指定年份的总页数
        :param year: 动漫年份
        :return: 总页数 (int)
        """
        url = f"https://bangumi.tv/anime/browser/airtime/{year}"
        for _ in range(3):  # 重试3次
            try:
                response = requests.get(url, headers=HEADERS, timeout=15)
                response.encoding = response.apparent_encoding
                soup = BeautifulSoup(response.text, 'lxml')
                page_info = soup.select_one("#columnSubjectBrowserA > div.section > div > div > span")
                if page_info:
                    text = page_info.text.strip()
                    return int(text.split('/')[-1].replace(")", "").strip())
                return 1
            except requests.exceptions.RequestException as e:
                print(f"请求失败,正在重试:{e}")
                time.sleep(5)
        print("获取总页数失败。")
        return 0
    
    
    def scrape_titles(year, total_pages):
        """
        爬取指定年份所有动漫标题
        :param year: 动漫年份
        :param total_pages: 总页数
        :return: 番剧标题列表
        """
        titles = []
        base_url = f"https://bangumi.tv/anime/browser/airtime/{year}?page="
    
        for page in range(1, total_pages + 1):
            url = base_url + str(page)
            print(f"正在爬取第 {page}/{total_pages} 页:{url}")
            retries = 3  # 最大重试次数
            success = False
    
            while retries > 0 and not success:
                try:
                    response = requests.get(url, headers=HEADERS, timeout=15)
                    response.encoding = response.apparent_encoding
                    soup = BeautifulSoup(response.text, 'lxml')
    
                    # 提取标题
                    page_titles = [h3.find('a').text.strip() for h3 in soup.find_all('h3') if h3.find('a')]
                    titles.extend(page_titles)
                    success = True  # 爬取成功
                    time.sleep(random.uniform(1, 3))  # 随机延迟
                except requests.exceptions.RequestException as e:
                    retries -= 1
                    print(f"请求失败,剩余重试次数:{retries},错误信息:{e}")
                    time.sleep(5)
    
            if not success:
                print(f"跳过第 {page} 页。")
        return titles
    
    
    def create_folders(year, titles):
        """
        创建年份文件夹及对应番剧文件夹
        :param year: 动漫年份
        :param titles: 番剧标题列表
        """
        year_folder = os.path.join(os.getcwd(), str(year))
        if not os.path.exists(year_folder):
            os.makedirs(year_folder)
    
        print(f"创建年份文件夹:{year_folder}")
        for title in titles:
            # 替换非法字符
            folder_name = sanitize_filename(title)
            folder_path = os.path.join(year_folder, folder_name)
            try:
                os.makedirs(folder_path, exist_ok=True)
            except OSError as e:
                print(f"创建文件夹失败:{folder_path},错误:{e}")
        print(f"已为 {len(titles)} 部番剧生成文件夹。")
    
    
    if __name__ == "__main__":
        # 1. 用户交互输入年份
        year = input("请输入要爬取的年份:").strip()
        if not year.isdigit():
            print("请输入有效的年份!")
            exit()
        year = int(year)
    
        # 2. 确定页数并爬取
        print(f"正在获取 {year} 年的总页数...")
        total_pages = get_total_pages(year)
        if total_pages == 0:
            print("无法获取总页数,程序终止。")
            exit()
        print(f"{year} 年共有 {total_pages} 页。")
    
        print("开始爬取番剧标题...")
        titles = scrape_titles(year, total_pages)
        print(f"爬取完成,共获取 {len(titles)} 部番剧标题。")
    
        # 3. 保存到文件
        output_file = f"{year}_anime_titles.txt"
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write("\n".join(titles))
        print(f"所有番剧标题已保存到文件:{output_file}")
    
        # 4. 是否生成文件夹
        create_folders_choice = input("是否为番剧生成文件夹?(y/n):").strip().lower()
        if create_folders_choice == 'y':
            create_folders(year, titles)
        else:
            print("已跳过文件夹生成。")

    现在可以自动生成文件夹了,帮我做番剧媒体库的框架

    • Mimosa233
      博主
      匿名
      iPhone Safari
      14 小时前
      2025-1-21 18:20:35

      妙啊!٩(ˊᗜˋ*)و

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯
 ̄﹃ ̄
(/ω\)
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
(´っω・`。)
( ,,´・ω・)ノ)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•)
(ㆆᴗㆆ)
有希酱最可爱啦!(素材来自bilibili@最上川下山)
整活by Mimosa233
Source: github.com/k4yt3x/flowerhd
galgame系列表情by Mimosa233
颜文字
周防有希
小恐龙
夸夸我!
花!
可愛い!
上一篇
下一篇