7 古典的な Python クローラー ケースのコード共有

今回の 7 つの Python クローラーの小さなケースには、再正則化、Xpath、美しいスープ、Selenium などの知識ポイントが含まれており、Python クローラーを始めたばかりの友人が参照学習するのに非常に適しています。注: 著作権またはプライバシーの問題が関係している場合は、削除するために間に合うように私に連絡してください。

1. 正規表現とファイル操作を使用して、投稿のコンテンツ全体をクロールし、「Xuba」に保存します (投稿は 5 ページ未満であってはなりません)。

今回はとあるバーのNBAバーの投稿を選択しました、投稿のタイトルは「クレイとハーデン、どちらが歴史的地位が高いか」です。クロールの対象となるのは投稿内の返信内容です。

ソース プログラムと主要な結果のスクリーンショット:

import csv
import requests
import re
import time

def main(page):
    url = f'https://tieba.baidu.com/p/7882177660?pn={page}'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
    }
    resp = requests.get(url,headers=headers)
    html = resp.text
    # 评论内容
    comments = re.findall('style="display:;">                    (.*?)</div>',html)
    # 评论用户
    users = re.findall('class="p_author_name j_user_card" href=".*?" target="_blank">(.*?)</a>',html)
    # 评论时间
    comment_times = re.findall('楼</span><span class="tail-info">(.*?)</span><div',html)
    for u,c,t in zip(users,comments,comment_times):
        # 筛选数据,过滤掉异常数据
        if 'img' in c or 'div' in c or len(u)>50:
            continue
        csvwriter.writerow((u,t,c))
        print(u,t,c)
    print(f'第{page}页爬取完毕')

if __name__ == '__main__':
    with open('01.csv','a',encoding='utf-8')as f:
        csvwriter = csv.writer(f)
        csvwriter.writerow(('评论用户','评论时间','评论内容'))
        for page in range(1,8):  # 爬取前7页的内容
            main(page)
            time.sleep(2)

2. マルチスレッド クローラーを実装して、小説のいくつかの章をクロールし、データベースに保存します (10 章以上)。

今回選択した小説サイトは小説サイトで、ここでは最初にクロールする小説を選択します

次に、Web ページのソース コードを分析して、小説の各章のリンクを分析します。

リンクの場所を見つけたら、XPath を使用してリンクと各章のタイトルを抽出します。

ここでは、リクエストを使用してリクエストを複数回送信する必要があるため、後で使用できるようにこれを関数にカプセル化します。

各章のリンクを取得した後、分析のために小説の章コンテンツページに入ります。

ウェブページの解析により、小説の内容は静的データであるウェブページのソースコードに含まれます。

ここでは、データ抽出に再正規表現を使用し、最終結果をクリーンアップします。

次に、データをデータベースに保存する必要があります。ここでは、クロールされたデータを mysql データベースに保存し、最初にデータベースの操作をシールします。

次に、クロールしたデータを保存します

最後のステップは、マルチスレッドを使用してクローラーの効率を向上させることです。ここでは、5 つのスレッドのスレッド プールを作成します。

ソースコードと結果のスクリーンショット:

import requests
from lxml import etree
import re
import pymysql
from time import sleep
from concurrent.futures import ThreadPoolExecutor

def get_conn():
    # 创建连接
    conn = pymysql.connect(host="127.0.0.1",
                           user="root",
                           password="root",
                           db="novels",
                           charset="utf8")
    # 创建游标
    cursor = conn.cursor()
    return conn, cursor

def close_conn(conn, cursor):
    cursor.close()
    conn.close()

def get_xpath_resp(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'}
    resp = requests.get(url, headers=headers)
    tree = etree.HTML(resp.text)  # 用etree解析html
    return tree,resp

def get_chapters(url):
    tree,_ = get_xpath_resp(url)
    # 获取小说名字
    novel_name = tree.xpath('//*[@id="info"]/h1/text()')[0]
    # 获取小说数据节点
    dds = tree.xpath('/html/body/div[4]/dl/dd')
    title_list = []
    link_list = []
    for d in dds[:15]:
        title = d.xpath('./a/text()')[0]  # 章节标题
        title_list.append(title)
        link = d.xpath('./a/@href')[0]   # 章节链接
        chapter_url = url +link  # 构造完整链接
        link_list.append(chapter_url)
    return title_list,link_list,novel_name

def get_content(novel_name,title,url):
    try:
        cursor = None
        conn = None
        conn, cursor = get_conn()
        # 插入数据的sql
        sql = 'INSERT INTO novel(novel_name,chapter_name,content) VALUES(%s,%s,%s)'
        tree,resp = get_xpath_resp(url)
        # 获取内容
        content = re.findall('<div id="content">(.*?)</div>',resp.text)[0]
        # 对内容进行清洗
        content = content.replace('<br />','\n').replace('&nbsp;',' ').replace('全本小说网 www.qb5.tw,最快更新<a href="https://www.qb5.tw/book_116659/">宇宙职业选手</a>最新章节!<br><br>','')
        print(title,content)
        cursor.execute(sql,[novel_name,title,content])  # 插入数据
        conn.commit()  # 提交事务保存数据
    except:
        pass
    finally:
        sleep(2)
        close_conn(conn, cursor)  # 关闭数据库


if __name__ == '__main__':
    # 获取小说名字,标题链接,章节名称
    title_list, link_list, novel_name = get_chapters('https://www.qb5.tw/book_116659/')
    with ThreadPoolExecutor(5) as t:  # 创建5个线程
        for title,link in zip(title_list,link_list):
            t.submit(get_content, novel_name,title,link)  # 启动线程

3. XPath と Beautiful Soup4 をそれぞれ使用して、https://movie.douban.com/ など、非同期で読み込まれない「特定のリーダーボード」の名前、説明、評価、レビュー者数などのデータをクロールして保存します。トップ250。

先分析:

まず、あるTop250ページにアクセスし、まずXPathバージョンを使用してデータを取得し、まず映画リストページのデータ構造を分析し、それを静的データに属するWebページのソースコードに投稿します

次に、データの法則を見つけ、xpath を使用して各映画のリンクと映画名を抽出します。

次に、リンクをクリックして詳細ページに入ります

詳細ページでデータを分析すると、それも静的データであることがわかり、引き続き xpath を使用してデータを抽出します

最後に、クロールされたデータを保存します。ここでは、保存に csv ファイルを使用します。

次にBeautiful Soup4のバージョンですが、ここではBS4のetreeを直接利用して動画一覧ページのデータ抽出を行っています。

最後に、データ ストレージにも csv ファイルを使用します

ソース コードは結果のスクリーンショットです。

XPath バージョン:

import re
from time import sleep
import requests
from lxml import etree
import random
import csv

def main(page,f):
    url = f'https://movie.douban.com/top250?start={page*25}&filter='
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',}
    resp = requests.get(url,headers=headers)
    tree = etree.HTML(resp.text)
    # 获取详情页的链接列表
    href_list = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/a/@href')
    # 获取电影名称列表
    name_list = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
    for url,name in zip(href_list,name_list):
        f.flush()  # 刷新文件
        try:
            get_info(url,name)  # 获取详情页的信息
        except:
            pass
        sleep(1 + random.random())  # 休息
    print(f'第{i+1}页爬取完毕')

def get_info(url,name):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',
        'Host': 'movie.douban.com',
    }
    resp = requests.get(url,headers=headers)
    html = resp.text
    tree = etree.HTML(html)
    # 导演
    dir = tree.xpath('//*[@id="info"]/span[1]/span[2]/a/text()')[0]
    # 电影类型
    type_ = re.findall(r'property="v:genre">(.*?)</span>',html)
    type_ = '/'.join(type_)
    # 国家
    country = re.findall(r'地区:</span> (.*?)<br',html)[0]
    # 上映时间
    time = tree.xpath('//*[@id="content"]/h1/span[2]/text()')[0]
    time = time[1:5]
    # 评分
    rate = tree.xpath('//*[@id="interest_sectl"]/div[1]/div[2]/strong/text()')[0]
    # 评论人数
    people = tree.xpath('//*[@id="interest_sectl"]/div[1]/div[2]/div/div[2]/a/span/text()')[0]
    print(name,dir,type_,country,time,rate,people)  # 打印结果
    csvwriter.writerow((name,dir,type_,country,time,rate,people))  # 保存到文件中

if __name__ == '__main__':
    # 创建文件用于保存数据
    with open('03-movie-xpath.csv','a',encoding='utf-8',newline='')as f:
        csvwriter = csv.writer(f)
        # 写入表头标题
        csvwriter.writerow(('电影名称','导演','电影类型','国家','上映年份','评分','评论人数'))
        for i in range(10):  # 爬取10页
            main(i,f)  # 调用主函数
            sleep(3 + random.random())

Beautiful Soup4****版:

import random
import urllib.request
from bs4 import BeautifulSoup
import codecs
from time import sleep

def main(url, headers):
    # 发送请求
    page = urllib.request.Request(url, headers=headers)
    page = urllib.request.urlopen(page)
    contents = page.read()
    # 用BeautifulSoup解析网页
    soup = BeautifulSoup(contents, "html.parser")
    infofile.write("")
    print('爬取豆瓣电影250: \n')

    for tag in soup.find_all(attrs={"class": "item"}):
        # 爬取序号
        num = tag.find('em').get_text()
        print(num)
        infofile.write(num + "\r\n")
        # 电影名称
        name = tag.find_all(attrs={"class": "title"})
        zwname = name[0].get_text()
        print('[中文名称]', zwname)
        infofile.write("[中文名称]" + zwname + "\r\n")
        # 网页链接
        url_movie = tag.find(attrs={"class": "hd"}).a
        urls = url_movie.attrs['href']
        print('[网页链接]', urls)
        infofile.write("[网页链接]" + urls + "\r\n")
        # 爬取评分和评论数
        info = tag.find(attrs={"class": "star"}).get_text()
        info = info.replace('\n', ' ')
        info = info.lstrip()
        print('[评分评论]', info)
        # 获取评语
        info = tag.find(attrs={"class": "inq"})
        if (info):  # 避免没有影评调用get_text()报错
            content = info.get_text()
            print('[影评]', content)
            infofile.write(u"[影评]" + content + "\r\n")
        print('')


if __name__ == '__main__':
    # 存储文件
    infofile = codecs.open("03-movie-bs4.txt", 'a', 'utf-8')
    # 消息头
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
    # 翻页
    i = 0
    while i < 10:
        print('页码', (i + 1))
        num = i * 25  # 每次显示25部 URL序号按25增加
        url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter='
        main(url, headers)
        sleep(5 + random.random())
        infofile.write("\r\n\r\n")
        i = i + 1
    infofile.close()

4. 特定の East Mall にある商品のレビュー データをクロールします (レビュー データはレビューの内容、時間、評価を含めて 100 件以上あります)。

先分析:

今回は某サイトの公式サイトでLenovoのノートパソコンを選択しましたが、データは動的に読み込まれ、開発者ツールでデータを取得して分析することができます。

ソースコードと結果のスクリーンショット:

import requests
import csv
from time import sleep
import random

def main(page,f):
    url = 'https://club.jd.com/comment/productPageComments.action'
    params = {
        'productId': 100011483893,
        'score': 0,
        'sortType': 5,
        'page': page,
        'pageSize': 10,
        'isShadowSku': 0,
        'fold': 1
    }
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',
        'referer': 'https://item.jd.com/'
    }
    resp = requests.get(url,params=params,headers=headers).json()
    comments = resp['comments']
    for comment in comments:
        content = comment['content']
        content = content.replace('\n','')
        comment_time = comment['creationTime']
        score = comment['score']
        print(score,comment_time,content)
        csvwriter.writerow((score,comment_time,content))
    print(f'第{page+1}页爬取完毕')

if __name__ == '__main__':
    with open('04.csv','a',encoding='utf-8',newline='')as f:
        csvwriter = csv.writer(f)
        csvwriter.writerow(('评分','评论时间','评论内容'))
        for page in range(15):
                main(page,f)
                sleep(5+random.random())

5. Huhu へのログインをシミュレートする複数のメソッドを実装し、江漢大学に関連する質問と回答をクロールします。

まず Selenium を使用して特定のログイン ページを開き、次に携帯電話を使用して QR コードをスキャンしてログインします。

ページに入ったら、開発者ツールを開いて要素を見つけ、入力ボックスを見つけて「Hanjiang University」と入力し、検索ボタンをクリックします。

2 番目の投稿を元素分析の例として取り上げます。

ソースコードと結果のスクリーンショット:

from time import sleep
from selenium.webdriver.chrome.service import Service
from selenium.webdriver import Chrome,ChromeOptions
from selenium.webdriver.common.by import By
import warnings

def main():
    #忽略警告
    warnings.filterwarnings("ignore")
    # 创建一个驱动
    service = Service('chromedriver.exe')
    options = ChromeOptions()
    # 伪造浏览器
    options.add_experimental_option('excludeSwitches', ['enable-automation','enable-logging'])
    options.add_experimental_option('useAutomationExtension', False)
    # 创建一个浏览器
    driver = Chrome(service=service,options=options)
    # 绕过检测
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
               Object.defineProperty(navigator, 'webdriver', {
               get: () => false
               })
           """
    })
    # 打开知乎登录页面
    driver.get('https://www.zhihu.com/')
    sleep(30)
    # 点击搜索框
    driver.find_element(By.ID,'Popover1-toggle').click()
    # 输入内容
    driver.find_element(By.ID,'Popover1-toggle').send_keys('汉江大学')
    sleep(2)
    # 点击搜索图标
    driver.find_element(By.XPATH,'//*[@id="root"]/div/div[2]/header/div[2]/div[1]/div/form/div/div/label/button').click()
    # 等待页面加载完
    driver.implicitly_wait(20)
    # 获取标题
    title = driver.find_element(By.XPATH,'//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/h2/div/a/span').text
    # 点击阅读全文
    driver.find_element(By.XPATH,'//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/span/div/button').click()
    sleep(2)
    # 获取帖子内容
    content = driver.find_element(By.XPATH,'//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/span[1]/div/span/p').text
    # 点击评论
    driver.find_element(By.XPATH,'//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/div[3]/div/div/button[1]').click()
    sleep(2)
    # 点击获取更多评论
    driver.find_element(By.XPATH,'//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div[2]/div/div/div[2]/div[2]/div/div[3]/button').click()
    sleep(2)
    # 获取评论数据的节点
    divs = driver.find_elements(By.XPATH,'/html/body/div[6]/div/div/div[2]/div/div/div/div[2]/div[3]/div')
    try:
        for div in divs:
            # 评论内容
            comment = div.find_element(By.XPATH,'./div/div/div[2]').text
            f.write(comment)  # 写入文件
            f.write('\n')
            print(comment)
    except:
        driver.close()

if __name__ == '__main__':
    # 创建文件存储数据
    with open('05.txt','a',encoding='utf-8')as f:
        main()

6. 学習した知識を総合的に使用して、特定のブログ ユーザーの Weibo コンテンツの最初の 5 ページをクロールします。

ここではクロール対象として人民日報の Weibo コンテンツを選択しましたが、規制に違反することを恐れて、ここには特定のページを掲載しません。

ソースコードと結果のスクリーンショット:

import requests
import csv
from time import sleep
import random

def main(page):
    url = f'https://weibo.com/ajax/statuses/mymblog?uid=2803301701&page={page}&feature=0&since_id=4824543023860882kp{page}'
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
        'cookie':'SINAGLOBAL=6330339198688.262.1661412257300; ULV=1661412257303:1:1:1:6330339198688.262.1661412257300:; PC_TOKEN=8b935a3a6e; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WWoQDW1G.Vsux_WIbm9NsCq5JpX5KMhUgL.FoMNShMN1K5ESKq2dJLoIpjLxKnL1h.LB.-LxKqLBoBLB.-LxKqLBKeLB--t; ALF=1697345086; SSOLoginState=1665809086; SCF=Auy-TaGDNaCT06C4RU3M3kQ0-QgmTXuo9D79pM7HVAjce1K3W92R1-fHAP3gXR6orrHK_FSwDsodoGTj7nX_1Hw.; SUB=_2A25OTkruDeRhGeFJ71UW-S7OzjqIHXVtOjsmrDV8PUNbmtANLVKmkW9Nf9yGtaKedmyOsDKGh84ivtfHMGwvRNtZ; XSRF-TOKEN=LK4bhZJ7sEohF6dtSwhZnTS4; WBPSESS=PfYjpkhjwcpEXrS7xtxJwmpyQoHWuGAMhQkKHvr_seQNjwPPx0HJgSgqWTZiNRgDxypgeqzSMsbVyaDvo7ng6uTdC9Brt07zYoh6wXXhQjMtzAXot-tZzLRlW_69Am82CXWOFfcvM4AzsWlAI-6ZNA=='
    }
    resp = requests.get(url,headers=headers)
    data_list = resp.json()['data']['list']
    for item in data_list:
        created_time = item['created_at']  # 发布时间
        author = item['user']['screen_name']   # 作者
        title = item['text_raw']   # 帖子标题
        reposts_count = item['reposts_count']  # 转发数
        comments_count = item['comments_count']  # 评论数
        attitudes_count = item['attitudes_count']  # 点赞数
        csvwriter.writerow((created_time,author,title,reposts_count,comments_count,attitudes_count))
        print(created_time,author,title,reposts_count,comments_count,attitudes_count)
    print(f'第{page}页爬取完毕')

if __name__ == '__main__':
    # 创建保存数据的csv文件
    with open('06-2.csv','a',encoding='utf-8',newline='')as f:
        csvwriter = csv.writer(f)
        # 添加文件表头
        csvwriter.writerow(('发布时间','发布作者','帖子标题','转发数','评论数','点赞数'))
        for page in range(1,6):  # 爬取前5页数据
            main(page)
            sleep(5+random.random())

7. 話題のトピックまたは興味のあるトピックを選択し、データをクロールして簡単なデータ分析を実行します (たとえば、映画の名前、種類、総興行収入、およびその他のデータをクロールして、映画の平均興行収入を統計的に分析します)さまざまな種類の映画、10 年間の年間興行収入のチャンピオン、興行収入の傾向など、中国の各省および地域の人口をクロールすることによる、私の国の人口分布の統計分析など)。

今回選択した URL は Yienyushu で、目的は内部の興行収入チャート データをクロールし、開発者ツールのパケット キャプチャ分析を通じてデータ インターフェイスを見つけて、データをキャプチャするコードの作成を開始することです。

ソースコードと結果のスクリーンショット:

import requests
import csv
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei'] #解决中文显示
plt.rcParams['axes.unicode_minus'] = False   #解决符号无法显示

def main():
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',}
    data = {
        'r': '0.9936776079863086',
        'top': '50',
        'type': '0',
    }
    resp = requests.post('https://ys.endata.cn/enlib-api/api/home/getrank_mainland.do', headers=headers, data=data)
    data_list = resp.json()['data']['table0']
    for item in data_list:
        rank = item['Irank']  # 排名
        MovieName = item['MovieName']  # 电影名称
        ReleaseTime = item['ReleaseTime']  # 上映时间
        TotalPrice = item['BoxOffice']   # 总票房(万)
        AvgPrice = item['AvgBoxOffice']   # 平均票价
        AvgAudienceCount = item['AvgAudienceCount']  # 平均场次
        # 写入csv文件
        csvwriter.writerow((rank,MovieName,ReleaseTime,TotalPrice,AvgPrice,AvgAudienceCount))
        print(rank,MovieName,ReleaseTime,TotalPrice,AvgPrice,AvgAudienceCount)

def data_analyze():
    # 读取数据
    data = pd.read_csv('07.csv')
    # 从上映时间中提取出年份
    data['年份'] = data['上映时间'].apply(lambda x: x.split('-')[0])
    # 各年度上榜电影总票房占比
    df1 = data.groupby('年份')['总票房(万)'].sum()
    plt.figure(figsize=(6, 6))
    plt.pie(df1, labels=df1.index.to_list(), autopct='%1.2f%%')
    plt.title('各年度上榜电影总票房占比')
    plt.show()
    # 各个年份总票房趋势
    df1 = data.groupby('年份')['总票房(万)'].sum()
    plt.figure(figsize=(6, 6))
    plt.plot(df1.index.to_list(), df1.values.tolist())
    plt.title('各年度上榜电影总票房趋势')
    plt.show()
    # 平均票价最贵的前十名电影
    print(data.sort_values(by='平均票价', ascending=False)[['年份', '电影名称', '平均票价']].head(10))
    # 平均场次最高的前十名电影
    print(data.sort_values(by='平均场次', ascending=False)[['年份', '电影名称', '平均场次']].head(10))


if __name__ == '__main__':
    # 创建保存数据的csv文件
    with open('07.csv', 'w', encoding='utf-8',newline='') as f:
        csvwriter = csv.writer(f)
        # 添加文件表头
        csvwriter.writerow(('排名', '电影名称', '上映时间', '总票房(万)', '平均票价', '平均场次'))
        main()
    # 数据分析
    data_analyze()

年間リストに含まれる映画の興行収入の割合から判断すると、2019年の割合が最も高く、2019年の映画の質が非常に良く、リストに載っている映画の数が多く、興行収入が高いことを示しています。

トレンドの観点から見ると、2016年から2019年にかけて、リストに掲載された映画の興行収入の合計は増加傾向にあり、2019年にピークに達しました。これは、今年の映画が非常に人気があることを示していますが、2020年から急激に減少しています。疫病の発生が年初から始まったため、旧正月ファイルが初期段階で公開されず、疫病の影響で映画館も閉鎖され、そのため、今年の興行収入は悲惨なものでした。

さて、今回の症例共有はこれで終わりです。初めて爬虫類を飼う方の参考になれば幸いです。

最後に、私の記事を注意深く読んでくださった皆様に感謝いたします。互恵性は常に必要です。以下の情報はあまり価値がありませんが、必要な場合は削除していただいてかまいません:

おすすめ

転載: blog.csdn.net/BlueSocks152/article/details/131145868