Python 网页抓取 - 动态网站

在本章中,让我们详细了解如何对动态网站进行网页抓取以及涉及的概念。


简介

网页抓取是一项复杂的任务,如果网站是动态的,复杂性会成倍增加。 根据联合国对 Web 可访问性的全球审计,超过 70% 的网站本质上是动态的,它们依赖 JavaScript 来实现其功能。


动态网站示例

让我们看一个动态网站的例子,了解为什么难以抓取。 这里我们将以从名为 http://example.webscraping.com/places/default/search 的网站进行搜索为例。 但是怎么能说这个网站是动态的呢? 可以从以下 Python 脚本的输出判断,该脚本将尝试从上述网页中抓取数据 −

import re
import urllib.request
response = urllib.request.urlopen('http://example.webscraping.com/places/default/search')
html = response.read()
text = html.decode()
re.findall('(.*?)',text)

输出

[ ]

上面的输出显示示例抓取器未能提取信息,因为我们试图查找的 <div> 元素是空的。


从动态网站抓取数据的方法

我们已经看到,抓取工具无法从动态网站抓取信息,因为数据是使用 JavaScript 动态加载的。 在这种情况下,我们可以使用以下两种技术从动态 JavaScript 依赖网站中抓取数据 −

  • 逆向工程 JavaScript
  • 呈现 JavaScript

逆向工程 JavaScript

称为逆向工程的过程会很有用,它可以让我们了解网页如何动态加载数据。

为此,我们需要单击指定 URL 的 inspect element 选项卡。 接下来,我们将单击 NETWORK 选项卡以查找对该网页发出的所有请求,包括路径为 /ajax 的 search.json。 除了从浏览器或通过 NETWORK 选项卡访问 AJAX 数据,我们也可以借助以下 Python 脚本来完成 −

import requests
url=requests.get('http://example.webscraping.com/ajax/search.json?page=0&page_size=10&search_term=a')
url.json() 

示例

上面的脚本允许我们使用 Python json 方法访问 JSON 响应。 类似地,我们可以下载原始字符串响应,并通过使用 python 的 json.loads 方法,我们也可以加载它。 我们在以下 Python 脚本的帮助下完成此操作。 它基本上会通过搜索字母表"a"中的字母然后迭代 JSON 响应的结果页面来抓取所有国家/地区。

import requests
import string
PAGE_SIZE = 15
url = 'http://example.webscraping.com/ajax/' + 'search.json?page={}&page_size={}&search_term=a'
countries = set()
for letter in string.ascii_lowercase:
   print('Searching with %s' % letter)
   page = 0
   while True:
   response = requests.get(url.format(page, PAGE_SIZE, letter))
   data = response.json()
   print('adding %d records from the page %d' %(len(data.get('records')),page))
   for record in data.get('records'):countries.add(record['country'])
   page += 1
   if page >= data['num_pages']:
      break
   with open('countries.txt', 'w') as countries_file:
   countries_file.write('n'.join(sorted(countries))) 

运行上述脚本后,我们将得到以下输出,记录将保存在名为 countries.txt 的文件中。

输出

Searching with a
adding 15 records from the page 0
adding 15 records from the page 1
...

呈现 JavaScript

在上一节中,我们对网页进行了逆向工程,了解 API 的工作原理以及我们如何使用它在单个请求中检索结果。 然而,我们在进行逆向工程时会遇到以下困难 −

  • 有时候,逆向网站可能非常困难。 例如,如果网站是使用 Google Web Toolkit (GWT) 等高级浏览器工具制作的,那么生成的 JS 代码将是机器生成的,难以理解和逆向工程。

  • 一些更高级别的框架(如 React.js)可以通过抽象已经很复杂的 JavaScript 逻辑来使逆向工程变得困难。

解决上述困难的方法是使用浏览器渲染引擎解析HTML,应用CSS格式并执行JavaScript来显示网页。

示例

在此示例中,为了呈现 Java Script,我们将使用熟悉的 Python 模块 Selenium。 以下 Python 代码将在 Selenium 的帮助下呈现网页 −

首先,我们需要从 selenium 中导入 webdriver,如下所示 −

from selenium import webdriver

现在,提供我们根据要求下载的 web 驱动程序的路径 −

path = r'C:\\Users\\gaurav\\Desktop\\Chromedriver'
driver = webdriver.Chrome(executable_path = path)

现在,提供我们想要在现在由我们的 Python 脚本控制的网络浏览器中打开的 url。

driver.get('http://example.webscraping.com/search')

现在,我们可以使用搜索工具箱的 ID 来设置要选择的元素。

driver.find_element_by_id('search_term').send_keys('.')

接下来我们可以使用 js 脚本来设置选择框内容如下 −

js = "document.getElementById('page_size').options[1].text = '100';"
driver.execute_script(js)

下面这行代码表示搜索已经准备好在网页上被点击了 −

driver.find_element_by_id('search').click()

下一行代码显示它将等待 45 秒以完成 AJAX 请求。

driver.implicitly_wait(45)

现在,要选择国家/地区链接,我们可以使用 CSS 选择器,如下所示 −

links = driver.find_elements_by_css_selector('#results a')

现在可以提取每个链接的文本以创建国家/地区列表 −

countries = [link.text for link in links]
print(countries)
driver.close()