クローラーは、Web サイト内のすべてのサブページのコンテンツを取得します。

前の記事では、ページ内の指定されたすべてのコンテンツをクロールする方法を紹介しましたが、この記事では、この Web サイトのすべてのサブページ内の指定されたすべてのコンテンツをクロールする方法について説明します。

必要な内容をコピペしたり、f12キーで必要なファイルのダウンロードアドレスを取得して一つずつダウンロードすれば良いという人もいるかもしれませんが、下図に示すように、最初は十数個あります。レベル × 数十の 2 レベル × 100 を超える病気 × 数百のコンテンツ = 数えたくないほどの桁違い... mp3 のサイズは 1 メガバイトを超えます。 mp3 は、すでにレベル T です。手動で 1 つずつダウンロードするには数か月かかると思います。そのため、この本が必要です。この記事で紹介されているマルチページ クローラーは自動的に取得され、少なくともそれは可能です数日以内に自動的にクロールされるようになります。

要件を確認する

ここに画像の説明を挿入します
このサイトでは、一次診療科と二次診療科の各疾患ごとの質問と、対応する医師が回答したmp3音声ファイルと音声コンテンツを取得したいので、各質問をクリックすると以下のページにアクセスできます
ここに画像の説明を挿入します
。ここでは、質問、医師、質問回答音声 mp3 ファイル、音声コンテンツなど、必要なものを入手できます。
まず、アイデアを確認します。各レベルを横断する –> 各レベル 2 を横断する –> 各疾患を横断する –> 各ページ (以下を参照) を横断する –> 各コンテンツをクリックして入力 –> 質問、医師、MP3、およびコンテンツを取得します
ここに画像の説明を挿入します
。上記は大まかな取得の枠組みであり、次に、保存形式を確認します: 各レベルはフォルダーです; このレベルのすべての第 2 レベルのディレクトリは各第 1 レベルのディレクトリに格納されます; 対応するすべてのファイルは各第 2 レベルのディレクトリに格納されます。 ; この病気の下にあるすべての mp3 ファイルを各病気ディレクトリに保存します。ファイル名は重複しないように「doctor-question」の形式になっています。各病気ディレクトリには txt ファイルが 1 つだけあり、これにはすべての mp3 ファイルが含まれます音声コンテンツ 後で該当するコンテンツをすぐに見つけられるように、各コンテンツには「Doctor-Question」形式のタイトルも追加されています。以下に示すように:
ここに画像の説明を挿入します
ここに画像の説明を挿入します

対応する要素を取得する

必要なものをもう一度見てみましょう: 質問、医師、mp3 ファイル、音声コンテンツです。次に、これら 4 つの対応する Web ページ要素の取得を開始します。これら 4 つだけを取得するわけではないことに注意してください。この Web サイトの 4 つのコンテンツをすべて自動的に取得するには、ページのコンテンツに必要な要素について、第 1 レベル、第 2 レベル、病気、次のページ (ページ変更) の各要素も見つける必要があります。 。

ここで、間違いがないように、探している各要素はページ上の唯一の要素であることに注意してください。たとえば、すべてのレベルがクラス「a」の div タグの下に配置されていますが、同一のタグが複数あります。その後、1 レベル上がると、クラス「a」のdiv タグがクラス「」の div タグの下にあることがわかりますb"。 div タグの下で、クラス "b"のdiv タグとクラス "a" の div タグを確認します。そのような組み合わせは 1 つだけあり、この組み合わせが必要な要素です。

第一レベルの部門

f12 で開発者モードを開き、矢印をクリックして要素を選択します:
ここに画像の説明を挿入します
ここで行う必要があるのは、この < dd > タグの下にあるすべての < a > タグを取得する方法です。フィルタリング後、最初の要素がクラス 'yslist_dq' の < div > タグ <dl> タグのすべての <a> タグは、すべての第 1 レベルの部門の要素です。必要なのは、a タグ内のテキストの内容です (ディレクトリを作成するためのディレクトリ名として) ) とタグの href 属性値 (次のレベルの部門にジャンプするために使用されます)。

中等部門

ここに画像の説明を挿入します
引き続き要素選択矢印を使用して、任意の二次部門をクリックします。図では、クラス「yslist_dq」を持つ <div> タグの下にある 2 番目の <dl> タグのすべての <a> タグが、すべての二次部門の要素であることがわかります。部門。 、タグ内のテキスト コンテンツと href 属性値を選択します。

病気のスクリーニング

ここに画像の説明を挿入します
同様に、クラス「yslist_dq」の <div> タグの下にある 3 番目の <dl> タグのすべての <a> タグがすべて疾患フィルタリング要素であり、テキスト コンテンツと href 属性値を選択していることがわかります。

すべてのページ

ここの各ページは、質問、医師、mp3、音声コンテンツという必要な 4 つのコンテンツを入手するためのページです。
ここに画像の説明を挿入します
要素選択矢印 (以下、矢印) を使用して任意の医師のアバターを選択してクリックし (1 つのアバターが質問に対応します)、ボックス内の URL をクリックして対応するページのコンテンツにジャンプします。次に、ページの要素は a タグの herf 属性値です
ここに画像の説明を挿入します
。まず a タグを取得します。
各 <li> タグがアバターであ​​ることがわかります。これらの <li> は、クラス "cur05" の <ul> タグの下にあります。<li> タグは、クラス "cur05" の <ul> タグの下にあることが確認されます。 " <a> タグは一意の組み合わせです。各 <li> タグの下には <a> タグが 1 つだけあるため、クラス "cur05" を持つ <ul> タグの下にあるすべての <a> タグのこの組み合わせが直接使用されます。 。

ページコンテンツ - 質問

ここに画像の説明を挿入します
画像にあるクラス「v_title」を持つ <h3> タグが唯一の要素であることが確認されており、この h3 タグが問題の要素であり、取得されるのはタグのテキスト コンテンツです。

ページコンテンツ - 医師

ここに画像の説明を挿入します
クラス「mgBottom10」の <ul> タグの下にある <strong> タグのテキスト コンテンツ。

ページコンテンツ - mp3 ファイル

ここに画像の説明を挿入します
<audio> は唯一のタグです。その src 属性値を取得します。

ページコンテンツ - 音声コンテンツ

ここに画像の説明を挿入します
クラス「v_con」の <div> タグの下にあるクラス「text」の <div> タグのテキスト コンテンツ。

次のページ

ここに画像の説明を挿入します
クラス「pageyl」の <div> タグの下にある最後の <li> タグの <a> タグの href 属性値。

コード

コードの紹介

1. コードの概要: 上記の確認要件で述べたように、各レベルのテキストと href 値を取得し、テキストを使用してディレクトリを作成し、href 値を使用してディレクトリにジャンプします。第 1 レベルの部門ページをトラバースし、同時に第 2 レベルと病気の管理を行い、次に病気の第 3 レベルにトラバースし、ジャンプするページの各アバターに対応する href 値の取得を開始し、必要な 4 を取得します。ジャンプしたページのコンテンツ内にある上記の要素に基づいて、次のページをクリックし、次のページがなくなるまで前の手順を繰り返します。
2. ヘッダーは元々私自身のブラウザから取得したものですが、1 つのヘッダーを使用して一度に数万のページにアクセスすると、クロール防止メカニズムがトリガーされて中断が発生する場合があるため、さらに数人がヘッダーを取得していることを見つけました。リストにあるものを選択し、ページに移動するたびにランダムに 1 つを選択します。十分なランダム シード値があることを確認するには、現在のタイムスタンプを使用します。ユーザー エージェントに加えて、リスト内の各ヘッダーには、中断の可能性を減らすために Cookie を追加することもできます (最初のヘッダーのコメントに示されているように)。
3. 一度にアクセスする人が多すぎるため、ページが応答しない場合があります。この場合、resolve() 関数を使用して Web サイトを周期的にリクエストし、ヘッダーをランダムに選択して各サイクルの応答時間を延長します。ページにジャンプすると例外が捕捉され、例外がある限りsolve()が呼び出されます。
4. たとえアンチクロール機構を回避する準備ができていたとしても、このような膨大な量のデータをクロールする際には、さまざまな要因によって中断が必ず発生します。数日間クロールしたデータが突然中断されたら、それが始まると、人々は本当に壊れてしまいます。機械に任せて操作するだけでもこんなに時間がかかるのに、待っている時間はかなりエネルギーの無駄ですよ、ハハハ。したがって、中断した時点からクロールを継続するために、ここでブレークポイント再開関数を記述する必要もあります (コード内のコメントは一目でわかります)。

コード

import random
import time
import requests
import os
from bs4 import BeautifulSoup

random.seed(int(time.time()))

def resolve(url, headers_list):
    for i in range(5):  # 循环去请求网站
        headers = random.choice(headers_list)
        response = requests.get(url, headers=headers, timeout=20)
        if response.status_code == 200:
            break
    return response

save_root = "/home/alpha/桌面/results"
url = "https://www....."
# headers = {
    
    
#     "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
# }
headers_list = [
    {
    
    
        # "Cookie":r"open_link_uuid=283daf44-d04e-49ae-bb47-3b8117d9e71f;utrace=23F72A5EEEB2DAFA0537A35D6848F563;Hm_lvt_8b53bc0f3e59f56a58a92a894280e28d=1693984791;open_link_uuid=47e1a39d-224f-4c45-b1a5-a6bdb84dce5b;Hm_lvt_8bb61372f543ea81f53e93693c2a7531=1694144572;Hm_lpvt_8b53bc0f3e59f56a58a92a894280e28d=1694425881",
        "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.76'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.202.400 QQBrowser/11.9.5355.400'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    }, {
    
    
        'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36'
    }
]
headers = random.choice(headers_list)
try:
    response = requests.get(url=url, headers=headers)
except:
    response = resolve(url, headers_list)
soup = BeautifulSoup(response.text, 'html.parser')
div_yslist_dq = soup.find('div', class_='yslist_dq')
first_dl = div_yslist_dq.find('dl')
a_tags_1 = first_dl.find_all('a')
for a_tag_1 in a_tags_1:     # 一级科室
    href_1 = a_tag_1.get('href')
    title_1 = a_tag_1.text
    ##############################
    # 中断 从断点继续
    # if title_1 in ["内科", "外科"]:
    # 	continue
    ##############################
    # print(f'链接地址: {href_1}, 标题: {title_1}')
    first_level = os.path.join(save_root, title_1)
    if not os.path.exists(first_level):
        os.mkdir(first_level)

    url = "https://www.youlai.cn" + href_1
    headers = random.choice(headers_list)
    try:
        response = requests.get(url=url, headers=headers)
    except:
        response = resolve(url, headers_list)
    soup = BeautifulSoup(response.text, 'html.parser')
    div_yslist_dq = soup.find('div', class_='yslist_dq')
    first_dl = div_yslist_dq.findAll('dl')[1]
    a_tags_2 = first_dl.find_all('a')
    for a_tag_2 in a_tags_2:      # 二级科室
        href_2 = a_tag_2.get('href')
        title_2 = a_tag_2.text
        if title_2 == "全部":
            continue
        ##############################
        # 中断 从断点继续
        # if title_2 in ["消化内科", "心血管内科"]:
        # 	continue
        ###############################
        # print(f'链接地址: {href_2}, 标题: {title_2}')
        sec_level = os.path.join(first_level, title_2)
        if not os.path.exists(sec_level):
            os.mkdir(sec_level)
    # input()
        url = "https://www.youlai.cn" + href_2
        headers = random.choice(headers_list)
        try:
            response = requests.get(url=url, headers=headers)
        except:
            response = resolve(url, headers_list)
        soup = BeautifulSoup(response.text, 'html.parser')
        div_yslist_dq = soup.find('div', class_='yslist_dq')
        first_dl = div_yslist_dq.findAll('dl')[2]
        a_tags_3 = first_dl.find_all('a')
        for a_tag_3 in a_tags_3:      # 疾病筛选
            href_3 = a_tag_3.get('href')
            title_3 = a_tag_3.text

            ##############################
            # 中断 从断点继续
            # if title_3 in ["胃炎", "胃溃疡", "肝硬化", "便秘", "肠胃炎", "腹痛", "慢性腹泻", "胰腺炎", "消化不良", "慢性胃炎", "反流性食管炎"]:
            #     continue
            ##############################

            if title_3 == "全部":
                continue
            # print(f'链接地址: {href_3}, 标题: {title_3}')
            third_level = os.path.join(sec_level, title_3)
            if not os.path.exists(third_level):
                os.mkdir(third_level)
            # print(href_3)
            url = "https://www.youlai.cn" + href_3
            headers = random.choice(headers_list)
            try:
                response = requests.get(url=url, headers=headers)
            except:
                response = resolve(url, headers_list)
            soup = BeautifulSoup(response.text, 'html.parser')

            page_num = soup.find('div', class_='pageyl').findAll("li")[-2].text
            # print(page_num)
            # input()
            for page in range(eval(page_num)):     # 遍历所有页数

                ##############################
                # 中断 从断点继续
                # print(page + 1)
                # if title_3 == "结肠炎" and page in [0, 1, 2]:
                #     page_url = "https://www.youlai.cn" + "/ask/voicelist/1_12_12_0_4.html"
                #     headers = random.choice(headers_list)
                #     try:
                #         response = requests.get(url=page_url, headers=headers)
                #     except:
                #         response = resolve(page_url, headers_list)
                #     soup = BeautifulSoup(response.text, 'html.parser')
                #     continue
                ##############################

                # 默认在第一页
                div_yslist_dq = soup.find('ul', class_='cur05')
                a_tags_4 = div_yslist_dq.findAll('a')
                for a_tag_4 in a_tags_4:       # 遍历每页的所有mp3
                    href_4 = a_tag_4.get('href')
                    title_4 = a_tag_4.text
                    # print(f'链接地址: {href_4}, 标题: {title_4}')
                    url = "https://www.youlai.cn" + href_4
                    headers = random.choice(headers_list)
                    try:
                        response_4 = requests.get(url=url, headers=headers)
                    except:
                        response_4 = resolve(url, headers_list)
                    soup_4 = BeautifulSoup(response_4.text, 'html.parser')
                    mp3_tag = soup_4.find('audio')
                    mp3_path = mp3_tag.get("src").strip()
                    # while True:
                    #     try:
                    #         print(response_4.status_code)
                    #         print(mp3_tag)
                    #         mp3_path = mp3_tag.get("src").strip()
                    #         break
                    #     except AttributeError:
                    #         print(headers)
                    #         headers = random.choice(headers_list)
                    #         time.sleep(5)
                    #         print(headers)
                    headers = random.choice(headers_list)
                    try:
                        mp3_content = requests.get(url=mp3_path, headers=headers).content
                    except:
                        mp3_content = resolve(mp3_path, headers_list)

                    mp3_title = soup_4.find('h3', class_='v_title').text.strip()
                    print(mp3_title)
                    if mp3_title[-1] == "?":
                        mp3_title = mp3_title[:-1]
                    doc_name = soup_4.find("ul", class_='mgBottom10').find("strong").text.strip()
                    with open(third_level + "/" + doc_name + "-" + mp3_title + ".mp3", mode="wb") as f1:  # 下载每个mp3文件
                        f1.write(mp3_content)
                    print(mp3_path)
                    mp3_text = soup_4.find('div', class_='v_con').find('div', class_='text').text.strip() + "\n"
                    with open(third_level + "/" + "content.txt", mode="a") as f2:
                        mp3_title = doc_name + "-" + mp3_title + "\n"
                        f2.write(mp3_title)
                        f2.writelines(mp3_text)
                    print(mp3_text)

                # 点击下一页
                next_page = soup.find('div', class_='pageyl').findAll("li")[-1]
                next_page_title = next_page.text
                print(next_page_title)
                if not next_page_title == "下一页":
                    continue
                next_page_href = next_page.find("a").get("href")
                print(next_page_href)
                page_url = "https://www.youlai.cn" + next_page_href
                headers = random.choice(headers_list)
                try:
                    response = requests.get(url=page_url, headers=headers)
                except:
                    response = resolve(page_url, headers_list)
                soup = BeautifulSoup(response.text, 'html.parser')

効果を実感

ここに画像の説明を挿入します
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/weixin_45354497/article/details/133017454