技術共有

【クローラ】クローラの基本

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina


1.HTTPレスポンスとリクエスト

HTTP はクライアント/サーバー プロトコルであり、通信の 2 つの当事者はクライアントとサーバーです。クライアントは HTTP リクエストを送信し、サーバーはそのリクエストを受信して​​処理し、HTTP 応答を返します。

1.HTTPリクエスト

HTTP リクエストは、リクエスト行、リクエスト ヘッダー、空行、およびリクエスト データ (POST リクエストのフォーム データなど) で構成されます。

  • 要求行には、要求メソッド、要求された URL、およびプロトコルのバージョンが含まれます。一般的なリクエスト メソッドには、GET、POST、PUT、DELETE などが含まれます。
  • リクエスト ヘッダーには、User-Agent、Accept、Content-Type など、クライアントとリクエストに関するその他の情報が含まれています。
  • 空行はリクエストヘッダーとリクエストデータを区切るために使用されます。
  • リクエスト データは通常、POST リクエストに使用され、送信されたデータが含まれます。

リクエスト例:

POST /api/users HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: application/json
Content-Type: application/json
Content-Length: 27

{
  "name": "John",
  "age": 30
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

リクエストライン:POST /api/users HTTP/1.1

リクエストヘッダー: Host、User-Agent、Accept、Content-Type、Content-Length などが含まれます。

空行: リクエストヘッダーとリクエストボディの間の空行

リクエストボディ:JSONデータ

2.HTTPレスポンス

HTTP 応答は、ステータス行、応答ヘッダー、空行、応答データで構成されます。

  • ステータス行には、プロトコルのバージョン、ステータス コード、およびステータス メッセージが含まれます。ステータス コードは、成功を示す 200、リソースが見つからないことを示す 404、内部サーバー エラーを示す 500 など、リクエストの処理結果を示します。
  • 応答ヘッダーには、サーバー、Content-Type、Content-Length など、サーバーと応答に関するその他の情報が含まれています。
  • 空行は、応答ヘッダーと応答データを区切るために使用されます。
  • 応答データには、HTML、JSON など、サーバーから返されたデータが含まれます。

サーバーが単純な HTML ページを返すと仮定すると、応答は次のようになります。

HTTP/1.1 200 OK
Date: Sun, 02 Jun 2024 10:20:30 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 137
Connection: keep-alive

<!DOCTYPE html>
<html>
<head>
    <title>Example Page</title>
</head>
<body>
    <h1>Hello, World!</h1>
    <p>This is a sample HTML page.</p>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

ステータスライン:HTTP/1.1 200 OK

応答ヘッダー: 日付、サーバー、コンテンツ タイプ、コンテンツの長さ、接続などが含まれます。

空行: レスポンスヘッダーとレスポンスボディの間の空行

レスポンスボディ: HTMLコードが含まれています

3. ステータスコード

HTTP ステータス コードは、サーバーによるリクエストの処理結果を示します。一般的なステータス コードは次のとおりです。

  • 1xx: 情報応答、リクエストが受信され、処理が続行されていることを示します。
  • 2xx: 成功、リクエストがサーバーによって正常に受信、理解され、受け入れられたことを示します。
  • 3xx: リダイレクト、リクエストを完了するにはさらなるアクションが必要であることを示します。
  • 4xx: クライアントエラー、サーバーがリクエストを処理できないことを示します。
  • 5xx: サーバーエラー、サーバーがリクエストを処理中にエラーが発生したことを示します。

ステータスコード

2. ライブラリをリクエストする

Python の Requests ライブラリは、非常に強力で使いやすい HTTP ライブラリです。

使用する前に、Requests ライブラリをインストールする必要があります。pip install requests

1. GET リクエストを開始する

GET リクエストは、サーバーにデータをリクエストするために使用されます。 Requests ライブラリを使用して GET リクエストを作成するのは非常に簡単です。

import requests
# 发起GET请求
response = requests.get('https://news.baidu.com')
# 检查响应状态码
if response.status_code == 200:
    # 打印响应内容
    print(response.text)
else:
    print(f"请求失败,状态码:{response.status_code}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2. POSTリクエストを開始します

POST リクエストは、サーバーにデータを送信するために使用されます。たとえば、ログインが必要な Web サイトでは、多くの場合、POST リクエストを使用してユーザー名とパスワードを送信します。 Requests ライブラリを使用して POST リクエストを開始する方法は次のとおりです。

import requests

# 定义要发送的数据
data = {
    'username': '123123123',
    'password': '1231231312'
}

# 发起POST请求
response = requests.post('https://passport.bilibili.com/x/passport-login/web/login', data=data)

# 检查响应状态码
if response.status_code == 200:
    # 打印响应内容
    print(response.text)
else:
    print(f"请求失败,状态码:{response.status_code}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3. プロセスリクエストヘッダー

一部の Web サイト (Douban など) では、クローラーに対してクロール防止メカニズムが許可されていないため、ブラウザーのふりをして認証を通過するには、HTTP 要求ヘッダーとパラメーターを設定する必要があります。

import requests

response = requests.get("https://movie.douban.com/top250")
if response.ok:
    print(response.text)
else:
    print("请求失败:" + str(response.status_code))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

要求が失敗しました

たとえば、上記のコードでリクエスト ヘッダーが設定されていない場合、Douban はアクセスを拒否します。

画像-20240607014319894

自由に Web サイトに入り、既製のユーザー エージェントを見つけて、それをリクエスト ヘッダーに含めることができます。

import requests

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}

response = requests.get("https://movie.douban.com/top250", headers=headers)
print(response.text)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

画像-20240607014435738

このようにして、Douban にアクセスし、Web ページのコンテンツを取得できます。

3. BeautifulSoupライブラリ

BeautifulSoup は、HTML および XML ドキュメントを解析するため、特に Web ページからデータを抽出するための Python ライブラリです。

使用する前に、BeautifulSoup ライブラリをインストールする必要があります。pip install beautifulsoup4

1. HTMLドキュメントを解析する

html.parserこれは Python の組み込みパーサーであり、ほとんどのシナリオに適しています。上記の Douban を例に挙げます。

import requests
from bs4 import BeautifulSoup

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}

response = requests.get("https://movie.douban.com/top250", headers=headers)
html = response.text
# 使用html.parser来解析HTML内容
soup = BeautifulSoup(html, "html.parser")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2. データの検索と抽出

BeautifulSoup は、HTML ドキュメントからデータを検索して抽出するための複数の方法を提供します。

BeautifulSoup の一般的なメソッド:

  • find(tag, attributes): 条件に一致する最初のタグを検索します。
  • find_all(tag, attributes): 一致するタグをすべて検索します。
  • select(css_selector): CSS セレクターを使用して、条件に一致するタグを検索します。
  • get_text(): ラベル内のテキスト コンテンツを取得します。
  • attrs: タグの属性辞書を取得します。

Ⅰ. 単一の要素を見つける

findメソッドは、基準を満たす最初の要素を見つけるために使用されます。たとえば、ページ内の最初のタイトルを検索するには、次のようにします。

title = soup.find("span", class_="title")
print(title.string)
  • 1
  • 2

Ⅱ.すべての要素を見つける

findAllメソッドは、基準を満たすすべての要素を検索するために使用されます。たとえば、ページ内のすべてのタイトルを検索するには:

all_titles = soup.findAll("span", class_="title")
for title in all_titles:
    print(title.string)
  • 1
  • 2
  • 3

Ⅲ.CSSセレクターを使用する

selectこのメソッドでは、CSS セレクターを使用して要素を検索できます。たとえば、すべてのタイトルを検索するには:

all_titles = soup.select("span.title")
for title in all_titles:
    print(title.get_text())
  • 1
  • 2
  • 3

IV. 要素の属性を取得する

使えるattrsプロパティ 要素の属性辞書を取得します。たとえば、すべての画像の URL を取得します。

all_images = soup.findAll("img")
for img in all_images:
    print(img['src'])
  • 1
  • 2
  • 3

4. Douban の映画リストをクロールする

画像-20240607021500369

映画のタイトル: HTML タグ名は「span」、指定された要素の class 属性は「title」です。

画像-20240607021403243

Rating: HTML タグはspan、指定された要素の class 属性は Rating_num です。

import requests
from bs4 import BeautifulSoup

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}
    response = requests.get(f"https://movie.douban.com/top250", headers=headers)
    html = response.text
    soup = BeautifulSoup(html, "html.parser")

    # 获取所有电影
    all_movies = soup.find_all("div", class_="item")

    for movie in all_movies:
        # 获取电影标题
        titles = movie.find_all("span", class_="title")
        for title in titles:
            title_string = title.get_text()
            if "/" not in title_string:
                movie_title = title_string

        # 获取电影评分
        rating_num = movie.find("span", class_="rating_num").get_text()

        # 输出电影标题和评分
        print(f"电影: {movie_title}, 评分: {rating_num}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

画像-20240607021144542

クロールは成功しましたが、最初のページのみがクロールされ、それ以降のコンテンツは正常にクロールされませんでした。上記の URL 接続を分析し、各ページは URL をstartパラメータはページングされます。

画像-20240607020345475

import requests
from bs4 import BeautifulSoup

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}

for start_num in range(0, 250, 25):
    response = requests.get(f"https://movie.douban.com/top250?start={start_num}", headers=headers)
    html = response.text
    soup = BeautifulSoup(html, "html.parser")

    # 获取所有电影条目
    all_movies = soup.find_all("div", class_="item")

    for movie in all_movies:
        # 获取电影标题
        titles = movie.find_all("span", class_="title")
        for title in titles:
            title_string = title.get_text()
            if "/" not in title_string:
                movie_title = title_string

        # 获取电影评分
        rating_num = movie.find("span", class_="rating_num").get_text()

        # 输出电影标题和评分
        print(f"电影: {movie_title}, 评分: {rating_num}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28