《從零開始學習Python爬蟲:頂點小說全網爬取實戰》

存子發表於2024-07-06

頂點小說

裝xpath helper

GitHub - mic1on/xpath-helper-plus: 這是一個xpath開發者的工具,可以幫助開發者快速的定位網頁元素。

Question:載入完外掛點選沒反應

Answer:將開發人員模式關閉即可

image-20240706100440880

爬蟲介紹

分類:

  • 搜尋引擎:爬取範圍廣
  • 聚焦爬蟲:爬取範圍聚焦

介紹:

程式發起請求(request),獲取響應(response),解析response中的資料

URL

即統一資源定位符

組成:

  • 協議
    • http
    • https:新增SSL協議(證書驗證),之前花錢,現在開源了,所以大部分網站都是https,據說效能損耗,但忽略不計
  • 主機IP地址(有時也包括埠號)
  • 主機資源具體地址:如目錄和檔名等

靜態網站和動態網站

  1. 靜態網站:資料在頁面原始碼中
  2. 動態網站:資料在介面中,網站透過ajax請求介面獲取資料,再透過js鑲在頁面中

結構化與非結構化資料

  1. 結構化資料:可以用關係型資料庫表示和儲存,表現為二維形式的資料
  2. 非結構化資料:資料結構不規則,不方便用二位邏輯來表現,如辦公文件、圖片、HTML、音訊和影片等等

xpath

概念:即為XML路徑語言(XML Path Language),用於確定XML文件中某部分位置的語言,python可以使用xpath的語法定位html文件中某部分的位置,並進行抽取

示例:xpath是抽取靜態網站資料的常用方法

Question:動態網站可以用xpath進行解析嗎?

Answer:

動態網站採用了ajax技術,ajax發起請求對頁面進行替換分為 整塊替換和 部分替換,部分替換則介面返回資料格式為Json,整塊替換則介面返回html文件

如果返回html文件,則可用xpath進行解析,如果返回Json資料,則不可用xpath進行解析

總結:xpath是否可進行解析取決於資料是否為結點資料(是否具有結點),JSON為字串,肯定不可用xpath進行解析

語法:

  • /:從文件根目錄開始選取
  • //:全域性進行查詢選取,//代表前面有東西,但不重要,相當於正規表示式的 .*?
  • //li/a/text():可以獲取小說的所有書名,然後透過python進行切片獲取具體的個別資料
  • //li//text():可以獲取 li 標籤下的所有文字(不區分什麼標籤),只限深度為1
  • //a[@class="poptext"]:可以選取帶有 class屬性 的 a標籤
  • @href:可以獲取此元素的 href屬性值
  • *:匹配任何元素結點,如//*、/bookstore/*
  • |:類似and,都獲取,如//book/title | //book/price

案例:頂點小說抓取

  1. 導包,定義headers(有的小說網站對headers無要求,有的有要求)
    import requests
    from lxml import etree
    import pymysql
    
    # headers
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
    }
    
  2. 獲取小說分類url
    # 獲取小說分類url
    def get_type():
        url = "https://www.cdbxs.com/sort/"
        source = requests.get(url=url, headers=headers).content.decode('utf-8')
        href_lists = etree.HTML(source).xpath('//ul[@class="nav"]/li/a/@href')[2:-4]
        type_lists = []
        for href in href_lists:
            type_lists.append(f"{url}{href.split('/')[2]}/1/")
        # print(type_lists)
        return type_lists
    
  3. 獲取最大頁
    # 獲取最大頁
    def get_max_page(first_page_url):
        source = requests.get(url=first_page_url, headers=headers).content.decode('utf-8')
        # print(source)
        max_page = etree.HTML(source).xpath('//a[13]/text()')
        return max_page
    
  4. 獲取每個分類的每一頁url
    # 獲取小說分類url
    type_lists = get_type()
    # 分類url預設為第一頁
    for first_page_url in type_lists:
        # 獲取帶分類的url的前半截
        type_url = first_page_url.split('1')[0]
        # 獲取此分類下最大頁
        max_page = get_max_page(first_page_url)
        # 生成此分類下每一頁url
        for every_page in range(1, int(max_page[0])+1):
            every_page_url = f"{type_url}{every_page}/"
            print(every_page_url)
    
  5. 獲取小說列表頁資訊
    def get_book_info(every_page_url):
        source = requests.get(url=every_page_url, headers=headers).content.decode('utf-8')
        book_lists = []
    
        lis = etree.HTML(source).xpath("//ul[@class='txt-list txt-list-row5']/li")
        for li in lis:
            book_id_url = li.xpath("span[@class='s2']/a/@href")[0]
            book_id = book_id_url.split('/')[3]
            # 書名
            book_name = li.xpath("span[@class='s2']/a/text()")[0]
            # 最新章節
            new_chapter = li.xpath("span[@class='s3']/a/text()")[0]
            # 作者
            author = li.xpath("span[@class='s4']/text()")[0]
            # 更新時間
            update_time = li.xpath("span[@class='s5']/text()")[0]
    
            source = requests.get(url=f"https://www.cdbxs.com{book_id_url}", headers=headers).content.decode('utf-8')
            # 字數
            font_num = etree.HTML(source).xpath("//p[6]/span/text()")[0]
            # 摘要
            summary = etree.HTML(source).xpath("//div[@class='desc xs-hidden']/text()")[0]
    
            # 以元組新增至 book_lists
            # print((book_id, book_name, new_chapter, author, update_time, font_num, summary))
            book_lists.append((book_id, book_name, new_chapter, author, update_time, font_num, summary))
        return book_lists
    
  6. 獲取章節列表url
    # 獲取章節列表url
    def get_chapter_urls(chapter_list_url):
        source = requests.get(url=chapter_list_url, headers=headers).content.decode('utf-8')
        # 章節url
        chapter_urls = map(lambda x: "https://www.cdbxs.com" + x, etree.HTML(source).xpath("//div[@class='section-box'][2]/ul[@class='section-list fix']/li/a/@href | //div[@class='section-box'][1]/ul[@class='section-list fix']/li/a/@href"))
    
        return chapter_urls
    
  7. 獲取章節詳情資訊
    # 獲取章節詳情資訊
    def get_chapter_info(chapter_url):
        source = requests.get(url=chapter_url, headers=headers).content.decode('utf-8')
        # 標題
        title = etree.HTML(source).xpath("//h1[@class='title']/text()")
        # 正文
        content = ''.join(etree.HTML(source).xpath("//div[@id='nb_content']/dd//text()"))
        if title:
            return title[0], content
        else:
            return '', content
    
  8. 整合
    # 獲取小說分類url
    type_lists = get_type()
    # 分類url預設為第一頁
    for first_page_url in type_lists:
        # 獲取帶分類的url的前半截
        type_url = first_page_url.split('1')[0]
        # 獲取此分類下最大頁
        max_page = get_max_page(first_page_url)
        # 生成此分類下每一頁url
        for every_page in range(1, int(max_page[0]) + 1):
            every_page_url = f"{type_url}{every_page}/"
            # 獲取小說列表頁資訊
            book_info_lists = get_book_info(every_page_url)
            # 獲取章節列表url
            for book_info in book_info_lists:
                print(f"爬取小說:{book_info[1]}...")
                book_id = book_info[0]
                chapter_urls = get_chapter_urls(f"https://www.cdbxs.com/booklist/b/{book_id}/1")
                for chapter_url in chapter_urls:
                    # print(chapter_url)
                    chapter_info = get_chapter_info(chapter_url)
                    print(chapter_info)
                    print(chapter_info[0])
                    print(chapter_info[1])
                    # print(f"title:{chapter_info[0]}")
                    # print(f"content:{chapter_info[1]}")
    

:關注我,後續會陸續出爬蟲內容(包括但不僅限於頂點小說進階:資料入庫、多執行緒多程序爬取資料)

更多精緻內容,關注公眾號:[CodeRealm]

相關文章