不幸的是,此特定服务不会向 API 发出任何直接请求 - 这将非常方便,因为这意味着您可以简单地模仿对同一 API 的请求,并且(很可能)返回易于解析的 JSON。
这个页面碰巧使用 ajax 并提供 .asp 文件 - 这意味着服务器执行一些脚本来生成实际的 HTML,然后它会为您提供服务。它也恰好为每个不同的表提供不同的 .asp 文件 - 基本上我是说这不是那么有趣。您可能必须为每个不同的表编写略有不同的解决方案才能获得所需的内容。
这是我想出的一个基础项目。您创建一个requests.Session 来跟踪cookie(在这种情况下,服务器恰好关心会话cookie)。然后,您对要抓取的每个表的 .asp 资源发出实际请求(在我的示例中,我只是抓取 AADT(年平均每日流量)表) - 每个表都有一个唯一的 URL,您可以通过查看浏览器的网络/资源/流量记录器。查看 Google Chrome 开发者工具中的 Query-String / Requests 参数,我选择了一些参数,它们似乎对您要抓取的特定记录很重要。然后我想出了一个粗略的列表理解,以使抓取的数据更具表现力 - 这部分可能会因每个表而有所不同。
def main():
import requests
from bs4 import BeautifulSoup
from itertools import groupby
session_url = "https://txdot.ms2soft.com/tcds/tsearch.asp"
session = requests.Session()
session_response = session.get(session_url)
session_response.raise_for_status()
aadt_url = "https://txdot.ms2soft.com/tcds/ajax/tcds_tdetail_aadt.asp"
params = {
"offset": "0",
"agency_id": "97",
"local_id": "57CC441"
}
aadt_response = session.get(aadt_url, params=params)
aadt_response.raise_for_status()
soup = BeautifulSoup(aadt_response.content, "html.parser")
rows = list(list(group) for key, group in groupby([item.find(text=True, recursive=False) for item in soup.find_all("td", {"class": "FormRow"})], key=lambda i: i and i.isprintable()) if key)
print(rows)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
[['2018', '1,482'], ['2017', '1,482', '246', '17'], ['2016', '1,488', '255', '17', '100'], ['2015', '1,199', '155', '13']]
>>>