Python 網路爬蟲 - 動態網站



在本章中,讓我們學習如何在動態網站上執行網路爬蟲以及所涉及的概念。

簡介

網路爬蟲是一項複雜的任務,如果網站是動態的,複雜性就會成倍增加。根據聯合國全球網路可訪問性審計,超過 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 點選“檢查元素”選項卡。接下來,我們將點選“網路”選項卡以查詢對該網頁發出的所有請求,包括具有“/ajax”路徑的 search.json。除了透過瀏覽器或透過“網路”選項卡訪問 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 driver 的路徑 -

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

現在,提供我們希望在現在由我們的 Python 指令碼控制的 web 瀏覽器中開啟的 url。

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

現在,我們可以使用搜索工具箱的 ID 將元素設定為選中。

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

接下來,我們可以使用 java 指令碼將選擇框內容設定為如下 -

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()
廣告