【问题标题】:Unable to locate website API for web-scraping - no json response found无法找到用于网络抓取的网站 API - 未找到 json 响应
【发布时间】:2020-07-16 10:23:30
【问题描述】:

我正在尝试从该网站上抓取理事会税阶数据,但找不到 API:

https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/services/counciltax/bandsearchresults.htm?action=pageNavigation&p=0

我浏览过 Stack Overflow 上的先前答案和文章,包括:

http://www.gregreda.com/2015/02/15/web-scraping-finding-the-api/

https://medium.com/@williamyeny/finding-ratemyprofessors-private-api-with-chrome-developer-tools-bd6747dd228d

我进入了 Network 标签 - XHR/All - Headers/ Preview/ Response,我唯一能找到的是:

/**/jQuery11130005376436794664263_1594893863666({ "html" : "<li class='navbar-text myprofile_salutation'>Welcome Guest!</li><li role='presentation' class=''><a href='https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/citizenportal/login.htm?redirect_url=https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/dashboard.htm'> Sign In / Register <span class='icon-arrow-right17 pull-right visible-xs-block'></span></a></li>" });

作为测试,我使用 AB24 4DE 进行搜索,但在 json 响应中的任何位置都找不到它。

据我所知,数据并未隐藏在网络套接字后面。

为此我运行了一个获取请求并得到:

JSONDecodeError: Expecting value: line 10 column 1 (char 19)

我错过了什么?

【问题讨论】:

    标签: json web-scraping


    【解决方案1】:

    看看网络工具,你做对了。我发现最好放大您在网络选项卡中给出的概述。您可以在浏览器中选择任何比例的操作。查看发生了哪些请求。因此,当您单击搜索时,您可以专注于请求和响应的开始。它为您提供了两个请求,一个将信息发布到服务器,一个将信息抓取到单独的 url。

    建议

    我看过该网站的建议是可能使用 selenium,这是一个模仿浏览器活动的包。下面你会看到我对请求的研究。本质上,该表单会在您每次进行搜索时生成一个唯一标记。您必须复制才能获得正确的响应。这很难提前知道。

    话虽如此,您可以使用 selenium 模拟浏览器活动并自动输入邮政编码并自动单击搜索按钮。然后,您可以获取页面源 HTML 并使用 beautifulsoup 对其进行解析。这是一个最小的可重现示例,向您展示了这一点。

    编码示例

    from selenium import webdriver 
    from bs4 import BeautifulSoup 
    driver = webdriver.Chrome(executable_path=r'c:\users\aaron\chromedriver.exe')
    url = 'https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/services/counciltax/bandsearch.htm'                          
    driver.get(url)
    driver.find_element_by_id('postcode').send_keys('AB24 4DE')
    driver.find_element_by_xpath('//input[@class="btn btn-primary cap-submit-inline"]').click()
    soup = BeautifulSoup(driver.page_source,'html.parser')
    

    还有让浏览器无头的空间,所以它不会弹出,你得到的只是解析后的 html。

    代码说明

    我们正在从 selenium 导入 webdriver,这提供了加载浏览器所需的模块。然后我们创建该 webdriver 的实例,在这种情况下,我使用的是 chrome,但您可以使用 firefox 或其他浏览器。

    您需要从这里下载 chromedriver,https://chromedriver.chromium.org/。 Webdriver 使用它来打开浏览器。

    我们使用get webdriver get方法让chromedriver去到我们想要的特定页面。

    Webdriver 有一个查找元素的列表...您可以使用。这里最简单的是find_element_by_id。我们可以在 HTML 中找到输入框的 id 用于输入邮政编码,这里我已经做到了。 Send_keys 将发送我们想要的任何文本,在本例中是 AB24 4DE。

    find_element_by_xpath 采用 XPATH 选择器。 '//' 遍历所有 DOM,我们选择输入,[@class=""] 部分选择特定的输入标记类。我们想要提交按钮。 click() 方法将点击该浏览器。

    一旦点击完成,我们就会获取页面源代码,这是必要的,因为我们随后将其输入到 BeuatifulSoup,它将为我们提供所需邮政编码的解析 HTML。

    对 HTTP 请求进行逆向工程

    下面是真正的教育,除非有人可以在向服务器发送请求之前获得唯一令牌。以下是网站在搜索表单方面的工作方式。

    从本质上看,它是将 cookie、标头、参数和数据发送到服务器。 cookie 有一个会话 ID,在我的测试中似乎没有改变。数据变量是您可以更改邮政编码的地方,但重要的是,每次您想要进行搜索时 ABCtoken 都会更改,并且参数是对服务器的检查,以确保它不是机器人。

    作为 HTTP POST 请求的示例。我们发送这个

    cookies = {
        'JSESSIONID': '1DBAC40138879EB418C14AD83C05AD86',
    }
    
    headers = {
        'Connection': 'keep-alive',
        'Cache-Control': 'max-age=0',
        'Upgrade-Insecure-Requests': '1',
        'Origin': 'https://ecitizen.aberdeencity.gov.uk',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-User': '?1',
        'Sec-Fetch-Dest': 'document',
        'Referer': 'https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/services/counciltax/bandsearch.htm',
        'Accept-Language': 'en-US,en;q=0.9',
    }
    
    params = (
                ('action', 'validateData'),
             )
    
    data = {
      'postcode': 'AB24 2RX',
      'address': '',
      'startSearch': 'Search',
      'ABCToken': '35fbfd26-cb4b-4688-ac01-1e35bcb8f63d'
    }
    

    https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/services/counciltax/bandsearch.htm

    然后它在这里使用相同的 JSESSIONID 和唯一的 ABCtoken 执行 HTTP GET 请求,以获取您想要的数据 bandsearchresults.html

    'https://ecitizen.aberdeencity.gov.uk/publicaccesslive/selfservice/services/counciltax/bandsearchresults.htm'
    

    所以它正在创建一个 JSESSIONID,这对于我的测试中的任何邮政编码似乎都是相同的。然后,当您使用相同的 JSESSIONID 并使用 ABCtoken 时,它会提供搜索结果 URL,您将获得正确的数据。

    【讨论】:

    • 感谢您的解释,非常有用,尤其是因为网络抓取不是我的强项。我以前尝试过使用 selenium,但是因为我正在使用 IBM Watson 并且 Python 是通过 conda 安装的,所以我还没有弄清楚如何在其中有效地安装 Chrome webdriver。 Stack Overflow 中已经有一个未解决的问题,没有任何回复。如果您有任何机会遇到答案,我将不胜感激。如果没有,我会将您的答案标记为已接受,并在弄清楚上述内容后返回。谢谢!
    • 如果您可以安装 selenium 包并指向其中包含 chromedriver.exe 的目录。你应该可以用 selenium 运行它。你能构建 conda 包吗? (docs.conda.io/projects/conda-build/en/latest/user-guide/…)。你有安装其他包吗?如果是这样,它们是如何安装的?
    • 我也很想知道在做之前是否有人能够获得出现在每个表单帖子中的 ABCtoken。它看不到使用 requests/beautifulsoup 等的方法。正如我在答案中所说,一旦你让 ABCtoken 使用 requests 和 beautifulsoup 获取数据,你就可以,但这是绊脚石。您似乎需要模拟浏览器活动来获得它。
    • 考虑了一会儿,也许值得一试 selenium-wire。它是 selenium 的一个附加组件,您可以在其中获取有关浏览器请求的详细信息。因此,您可以模拟搜索过程,获取 ABC 令牌,然后以高度结构化的方式获取数据。每次您想按邮政编码进行搜索时,都必须对其进行硬编码,但这是一个可行的选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-01
    • 1970-01-01
    • 2020-05-20
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    相关资源
    最近更新 更多