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

使用sys.argv的Python网页爬虫出现NoneType错误求助

TypeError: 'NoneType' object is not subscriptable 错误修复方案

问题背景

Python新手开发了一个基于sys.argv的volby.cz网站爬虫,计划通过终端传参运行,但执行时触发TypeError: 'NoneType' object is not subscriptable错误,错误定位在url_list函数的all_url = url_part + href.a["href"]代码行。

完整代码

import sys 
import requests 
import csv 
import os 
from bs4 import BeautifulSoup

def main():
    url = input_arguments()[0]
    filename = input_arguments()[1]
    header = summary_info(url)
    data = data_scraping(url)
    save_to_csv(filename, header, data)
    check_file(filename)

def url_list():
    url = "https://volby.cz/pls/ps2017nss/ps3?xjazyk=CZ"
    r = requests.get(url)
    soup = BeautifulSoup(r.text, "html.parser")
    list_href = []
    for index in range(1, 15):
        find = soup.find_all(("td", {"headers": f"{index}sa3"}))
        for href in find:
            url_part = "https://volby.cz/pls/ps2017nss/"
            all_url = url_part + href.a["href"]
            list_href.append(all_url)
    return list_href

def input_arguments():
    """
    Kontroluje a přijímá argumet URL a název výstupního souboru.
    """
    if len(sys.argv) != 3:
        print(
            f"+--------------------------------------------------------------------------+",
            f"| 1) Jako první argument zadej URL adresu v uvozovkách:                    |",
            f'|    např.: "https://volby.cz/pls/ps2017"                                  |',
            f"| 2) Jako druhý argument zadej název souboru pro uložení dat v uvozovkách: |",
            f'|    např.: "vysledky_lipno.csv"                                          |',
            f"| 3) Celkový vzor:                                                         |",
            f"+--------------------------------------------------------------------------+",
            f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
            f"+--------------------------------------------------------------------------+",
            sep="\n"
            )
        quit()
    elif sys.argv[2] in url_list():
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Jako prní argument zadej URL adresu a jako druhý název souboru.          |",
                f"| Argumenty zadej v uvozovkách a odděl mezerou.                            |",
                f"| Viz vzor:                                                                |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    elif sys.argv[1] not in url_list():
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Tato URL adresa není podporována.                                        |",
                f"| Zadej podporovanou URL adresu způsobem viz vzor:                         |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    elif not sys.argv[2].endswith('.csv'):
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Název souboru musí končit příponou .csv                                  |",
                f"| Viz vzor:                                                                |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    else:
        url = sys.argv[1]
        filename = sys.argv[2]
    return url, filename

def summary_info(url):
    """
    Vypíše názvy stran.
    """
    header = [
        "kód obce,"
        "název obce",
        "voliči v seznamu",
        "vydané obálky",
        "platé hlasy",
    ]

    first_page = requests.get(url)

    print(
        f"Stahuji informace z vybraného URL:",
        f"{url}",
        sep="\n"
    )

    soup = BeautifulSoup(first_page.text, "html.parser")
    first_page_href = soup.find("td", {"class": "cislo"}).a["href"]

    part_url = "https://volby.cz/pls/ps2017nss/"
    header_url = part_url + first_page_href

    second_page = requests.get(header_url)
    soup = BeautifulSoup(second_page.text, "html.parser")

    for name in (soup.find_all("td", {"class": "overflow_name"})):
        header.append(name.text)

    return header

def data_scraping(url):
    """
    ( This function allows download data)Funkce bere kód obce a název obce a přidává data do listu ze stránky vybrané
    uživatelem. Následně přistupuje přes kód obce ke zbylým datům, která přidává do listu.
    Jakmile má funkce všechna data pro danou obec/řádek, tak přidává list do listu data.
    Tento postup se opakuje pro všechny obce.
    """

    first_page = requests.get(url)

    print(
        f"stahuji data z vybraného URL:"
        f"(url",
        sep="\n"
        )

    soup = BeautifulSoup(first_page.text, "html.parser")

    first_page_code = soup.find_all("td", {"class": "cislo"})
    first_page_names = soup.find_all("td", {"class": "overflow_name"})

    if len(first_page_names) == 0:
        first_page_names = soup.find_all("td", {"headers": "t1sa1 t1sb2"})

    first_page_hrefs = [href.a["href"] for href in first_page_code]

    data = []

    part_url = "https://volby.cz/pls/ps2017nss/"

    for index, result in enumerate(first_page_hrefs, 0):
        row_list = []

        second_url = part_url + result

        second_page = requests.get(second_url)
        soup = BeautifulSoup(second_page.text, "html.parser")

        row_list.append(first_page_code[index].text)
        row_list.append(first_page_names[index].text)

        row_list.append((soup.find("td", {"headers": "sa2"}).text).replace('\xa0', ''))
        row_list.append((soup.find("td", {"headers": "sa3"}).text).replace('\xa0', ''))
        row_list.append((soup.find("td", {"headers": "sa6"}).text).replace('\xa0', ''))

        first_candidate_parties = (soup.find_all("td", {"headers": "t1sa2 t1sb3"}))
        for data_candidate in first_candidate_parties:
            row_list.append(data_candidate.text.replace('\xa0', ''))

        second_candidate_parties = (soup.find_all("td", {"headers": "t2sa2 t2sb3"}))
        for data_candidate in second_candidate_parties:
            numeric = (data_candidate.text.replace('\xa0', ''))
            if numeric.isnumeric():
                row_list.append(numeric)

        data.append(row_list)

    return data

def check_file(filename):
    dash = "-" * len(filename)

    if filename in os.listdir():
        print(
            f"+---------------------------{dash}+",
            f"| Data uložena do souboru: {filename} |",
            f"+---------------------------{dash}+",
            f"Ukončuji program...",
            sep="\n", end=("")
        )
    else:
        print(
            f"+--------------------{dash}+",
            f"| Soubor nenalezen: {filename} |",
            f"+--------------------{dash}+",
            f"Ukončuji program...",
            sep="\n", end=("")
        )

def save_to_csv(filename, header, data):
    """
    Uloží data do csv.
    """
    with open(filename, mode="w", newline="", encoding="utf-8") as data_csv:
        writer = csv.writer(data_csv)
        print(
            f"Ukládám data do vybraného souboru:",
            f"{filename}",
            sep="\n"
        )
        writer.writerow(header)
        for row in data:
            writer.writerow(row)

if __name__ == "__main__":
    main()

运行参数示例

  • 执行命令:py election_scraper.py
  • 传入带引号的URL:"https://volby.cz/pls/ps2017nss/ps311?xjazyk=CZ&xkraj=6&xobec=566403&xvyber=4204"
  • 传入带引号的CSV文件名:"vysledky_lipno.csv"

报错信息

Traceback (most recent call last):
  File "C:\Users\Admin\PycharmProjects\pythonProject3ElectionScraper\election_scraper.py", line 226, in <module>      
    main()
  File "C:\Users\Admin\PycharmProjects\pythonProject3ElectionScraper\election_scraper.py", line 16, in main
    url = input_arguments()[0]
  File "C:\Users\Admin\PycharmProjects\pythonProject3ElectionScraper\election_scraper.py", line 55, in input_arguments
    elif sys.argv[2] in url_list():
  File "C:\Users\Admin\PycharmProjects\pythonProject3ElectionScraper\election_scraper.py", line 33, in url_list       
    all_url = url_part + href.a["href"]
TypeError: 'NoneType' object is not subscriptable

错误原因

url_list函数中,遍历soup.find_all(("td", {"headers": f"{index}sa3"}))返回的<td>元素时,部分<td>标签内部未包含<a>标签,导致href.a返回None,此时尝试访问None["href"]就会触发该类型错误。

修复方案

1. 给url_list函数添加空值判断

修改url_list函数,在获取<a>标签的href属性前,先检查<a>标签是否存在:

def url_list():
    url = "https://volby.cz/pls/ps2017nss/ps3?xjazyk=CZ"
    r = requests.get(url)
    soup = BeautifulSoup(r.text, "html.parser")
    list_href = []
    for index in range(1, 15):
        find = soup.find_all(("td", {"headers": f"{index}sa3"}))
        for href in find:
            # 检查当前<td>是否包含<a>标签
            if href.a is not None:
                url_part = "https://volby.cz/pls/ps2017nss/"
                all_url = url_part + href.a["href"]
                list_href.append(all_url)
    return list_href

2. 优化参数校验逻辑(可选)

当前input_arguments函数多次调用url_list(),会重复发起HTTP请求,建议提前缓存支持的URL列表,减少请求次数:

def input_arguments():
    """
    Kontroluje a přijímá argumet URL a název výstupního souboru.
    """
    if len(sys.argv) != 3:
        print(
            f"+--------------------------------------------------------------------------+",
            f"| 1) Jako první argument zadej URL adresu v uvozovkách:                    |",
            f'|    např.: "https://volby.cz/pls/ps2017"                                  |',
            f"| 2) Jako druhý argument zadej název souboru pro uložení dat v uvozovkách: |",
            f'|    např.: "vysledky_lipno.csv"                                          |',
            f"| 3) Celkový vzor:                                                         |",
            f"+--------------------------------------------------------------------------+",
            f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
            f"+--------------------------------------------------------------------------+",
            sep="\n"
            )
        quit()
    # 提前获取一次支持的URL列表,避免重复请求
    supported_urls = url_list()
    filename = sys.argv[2]
    url = sys.argv[1]
    
    if filename in supported_urls:
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Jako prní argument zadej URL adresu a jako druhý název souboru.          |",
                f"| Argumenty zadej v uvozovkách a odděl mezerou.                            |",
                f"| Viz vzor:                                                                |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    elif url not in supported_urls:
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Tato URL adresa není podporována.                                        |",
                f"| Zadej podporovanou URL adresu způsobem viz vzor:                         |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    elif not filename.endswith('.csv'):
            print(
                f"+--------------------------------------------------------------------------+",
                f"| Název souboru musí končit příponou .csv                                  |",
                f"| Viz vzor:                                                                |",
                f"+--------------------------------------------------------------------------+",
                f'| election_scraper.py "https://volby.cz/pls/ps2017" "vysledky_lipno.csv"  |',
                f"+--------------------------------------------------------------------------+",
                sep="\n"
                )
            quit()
    else:
        return url, filename

3. 修复其他潜在风险点

代码中summary_info和data_scraping函数也存在直接访问.a["href"]的情况,建议统一添加空值判断,避免触发同类错误:
比如summary_info中的代码:

first_page_href = soup.find("td", {"class": "cislo"}).a["href"]

修改为:

cislo_td = soup.find("td", {"class": "cislo"})
if cislo_td and cislo_td.a:
    first_page_href = cislo_td.a["href"]
else:
    print("无法找到目标链接,程序退出")
    quit()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:24:18