python を使用してクロールする場合、一部の Web ページからの直接リクエストによって取得した HTML コードに、ブラウザーに表示される必要なデータが含まれていないことに遭遇することがあります。
これは、情報が Ajax を通じてロードされ、js レンダリングを通じて生成されるためです。現時点では、この Web ページのリクエストを分析する必要があります。
前回の記事では、Cookieとは何か、模擬ログインの動作の流れについて説明しましたが、今回はWebページのAjaxリクエストを解析する方法を紹介します。
Ajax とは
AJAX は「Asynchronous Javascript And XML」(非同期 JavaScript および XML) の略で、インタラクティブな Web アプリケーションのための Web 開発テクノロジの作成。
AJAX = 非同期 JavaScript および XML (標準ユニバーサル マークアップ言語のサブセット)。
AJAX は、高速で動的な Web ページを作成するためのテクノロジです。
AJAX は、Web ページ全体をリロードせずに Web ページの一部を更新できるテクノロジーです。
簡単に言うと、Web ページが読み込まれます。ブラウザのアドレス バーの URL は変更されていません。JavaScript (ajax である必要があります) によって非同期で読み込まれる Web ページです。 AJAX は通常、XMLHttpRequest オブジェクト インターフェイスを通じてリクエストを送信し、XMLHttpRequest は一般に XHR と省略されます。
Guoke.com Web サイトの分析
ターゲット Web サイトは Guoke.com を使用して分析されます。
この Web ページにはページめくりボタンがないことがわかります。リクエストをプルダウンし続けると、Web ページは自動的にさらにコンテンツを読み込みます。しかし、Web ページの URL を観察すると、Web ページの読み込みリクエストによって URL が変化していないことがわかります。そして、この URL を直接リクエストすると、明らかに最初のページの HTML コンテンツしか取得できません。
#では、すべてのページのデータを取得するにはどうすればよいでしょうか?
Chrome で開発者ツール (F12) を開きます。 「ネットワーク」をクリックし、「XHR」タブをクリックします。次に、Web ページを更新し、リクエストをプルダウンします。この時点で XHR タグが表示され、Web ページが読み込まれるたびにリクエストがポップアップ表示されます。
最初のリクエストをクリックすると、そのパラメータが表示されます:
retrieve_type:by_subject limit:20 offset:18 -:1500265766286
2 番目のリクエストをクリックすると、パラメータは次のとおりです:
retrieve_type:by_subject limit:20 offset:38 -:1500265766287
制限パラメータはそれぞれのページでロードされる記事の数を制限し、offset はページ数です。下を見ると、各リクエストのオフセット パラメーターが 20 ずつ増加していることがわかります。
次に、各リクエストの応答内容を確認します。これは、次の形式のデータです。結果ボタンをクリックすると、20件の記事のデータ情報が表示されます。このようにして、必要な情報の場所を見つけることができ、リクエストヘッダーに json データが格納されている URL アドレスが表示されます。 http://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=18
クロールプロセス
Ajaxリクエストを解析して各ページの記事URL情報を取得、各記事を解析して必要なデータを取得、取得したデータをデータベースに保存、複数のプロセスを起動してクロールピックの数が多い。Start
私たちのツールは引き続きリクエストと BeautifulSoup 解析を使用します。 まず、すべてのページの情報を取得するために Ajax リクエストを分析する必要があります。上記の Web ページの分析を通じて、Ajax によって読み込まれた json データの URL アドレスを取得できます:http://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=18
この URL を構築する必要があります。# 导入可能要用到的模块 import requests from urllib.parse import urlencode from requests.exceptions import ConnectionError # 获得索引页的信息 def get_index(offset): base_url = 'http://www.guokr.com/apis/minisite/article.json?' data = { 'retrieve_type': "by_subject", 'limit': "20", 'offset': offset } params = urlencode(data) url = base_url + params try: resp = requests.get(url) if resp.status_code == 200: return resp.text return None except ConnectionError: print('Error.') return None
import json # 解析json,获得文章url def parse_json(text): try: result = json.loads(text) if result: for i in result.get('result'): # print(i.get('url')) yield i.get('url') except: pass
既然获得了文章的url,那么对于获得文章的数据就显得很简单了。这里不在进行详细的叙述。我们的目标是获得文章的标题,作者和内容。
由于有的文章里面包含一些图片,我们直接过滤掉文章内容里的图片就好了。
from bs4 import BeautifulSoup # 解析文章页 def parse_page(text): try: soup = BeautifulSoup(text, 'lxml') content = soup.find('div', class_="content") title = content.find('h1', id="articleTitle").get_text() author = content.find('div', class_="content-th-info").find('a').get_text() article_content = content.find('div', class_="document").find_all('p') all_p = [i.get_text() for i in article_content if not i.find('img') and not i.find('a')] article = '\n'.join(all_p) # print(title,'\n',author,'\n',article) data = { 'title': title, 'author': author, 'article': article } return data except: pass
这里在进行多进程抓取的时候,BeautifulSoup也会出现一个错误,依然直接过滤。我们把得到的数据保存为字典的形式,方便保存数据库。
接下来就是保存数据库的操作了,这里我们使用Mongodb进行数据的存储。
import pymongo from config import * client = pymongo.MongoClient(MONGO_URL, 27017) db = client[MONGO_DB] def save_database(data): if db[MONGO_TABLE].insert(data): print('Save to Database successful', data) return True return False
我们把数据库的名字,和表名保存到config配置文件中,在把配置信息导入文件,这样会方便代码的管理。
最后呢,由于果壳网数据还是比较多的,如果想要大量的抓取,我们可以使用多进程。
from multiprocessing import Pool # 定义一个主函数 def main(offset): text = get_index(offset) all_url = parse_json(text) for url in all_url: resp = get_page(url) data = parse_page(resp) if data: save_database(data) if __name__ == '__main__': pool = Pool() offsets = ([0] + [i*20+18 for i in range(500)]) pool.map(main, offsets) pool.close() pool.join()
函数的参数offset就是页数了。经过我的观察,果壳网最后一页页码是 12758,有 637 页。这里我们就抓取 500 页。进程池的map方法和Python内置的map方法使用类似。
好了,对于一些使用Ajax加载的网页,我们就可以这么抓取了。
以上がJS によってレンダリングされた Web ページの Ajax リクエストを分析する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。