天天看點

Python--爬取百度貼吧電腦版

"""
author = 幻夜
mail = [email protected]
以貼吧的電腦端為爬取對象
由輸入的貼吧名字,爬取該貼吧下所有子貼的url,tiltle以及子貼下所有圖檔(不包含廣告,頭像類)
之前嘗試用xpath爬取,由于bug太多,最後放棄,采用了正規表達式去爬取
本代碼采取擷取一頁的所有資訊再去存儲,下載下傳,是以是50個存儲一次
該代碼進行多次不同貼吧嘗試,沒有報錯,希望廣大網友能夠發現bug并及時聯系我
版權聲明:本文為部落客原創文章,轉載請附上博文連結!
"""
import requests
import re
import json
import os
import time


class TiebaSipder(object):
    def __init__(self, tieba_name):

        self.part_url = "https://tieba.baidu.com"
        self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
                                      "Chrome/68.0.3440.106 Safari/537.36"}
        self.tieba_name = tieba_name
        # 貼吧第一次url
        self.start_url = "https://tieba.baidu.com/f?kw={}&pn=0".format(self.tieba_name)
        # 由于每次調用時都需要進行對正則進行處理,這裡進行預處理減少操作步驟
        # 下一頁的url
        self.next_url_pattern = re.compile('<a href="(.*?)" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="next pagination-item " >下一頁&gt;</a>')
        # 每一個子貼的url
        self.detailtie_url_pattern = re.compile(r'href="/p(.*)" target="_blank" rel="external nofollow"  title')
        # 每一個子貼的标題
        self.tie_title_pattern = re.compile('title=\"(.*)\" target=')
        # 進行預處理,分組,保證子貼title和url互相對應,不會出現某一項為空時錯位
        self.detail_list_pattern = re.compile("<a (.*?)</a>")
        # 圖檔的連結
        self.detail_img_pattern = re.compile('<img class="BDE_Image" src="(https://imgsa.baidu.com.*?)"')   # http://fc-feed.cdn.bcebos.com.* 為廣告
        # 子貼的下一頁
        self.detail_next_url_pattern = re.compile('<a href="(.*?)" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >下一頁')

    def parse_url(self, url):    # 獲得url的HTML檔案string類型
        response = requests.get(url, headers=self.headers)
        assert response.status_code == 200
        # 超過百分之99的貼吧response.encoding為utf-8編碼,但是電腦版貼吧會出現部分報錯UnicodeDecodeError
        # 添加gbk後還未出現報錯,如果出現報錯,請郵聯
        try:
            html_str = response.content.decode(response.encoding)
        except UnicodeDecodeError:
            html_str = response.content.decode("gbk")
        return html_str

    def get_content_list(self, html_str): #擷取url、title和圖檔連結并儲存在content_list中
        content_list = list()
        # 進行預處理,分組
        detail_url_list = re.findall(self.detail_list_pattern, html_str)


        for dul in detail_url_list:
            item = dict()
            try:
                # 從detail_url_list依次尋找
                item["detail_url"] = self.part_url + '/p' + re.search(self.detailtie_url_pattern, dul).group(1)
                # print(type(item["detail_url"]))
                item["tie_title"] = re.search(self.tie_title_pattern, dul).group(1)
                # print(type(item["tie_title"]))
            except AttributeError:
                # 由于經過預處理,有一些detail_url_list的元素并不會比對到,會報錯AttributeError,但不影響正常擷取,pass
                pass
            else:
                # 上面兩個鍵未比對到,就不進行下面一步,盡量優化,減少執行步驟
                try:
                    # 擷取子貼的所有圖檔(不包含廣告,頭像類)
                    item["img_list"] = self.get_list_img(item["detail_url"], [])
                    # print("IMG", item["img_list"])
                    # print("-"*100)
                except KeyError:
                    pass
            if item != {}:
                # PythOn 中對應包含三要素: ID、type、value
                # is 是通過id進行判斷,==是通過type和value進行判斷
                content_list.append(item)
            else:
                # 如果沒比對到,删除item
                del item
        try:
            next_url = 'https:' + re.search(self.next_url_pattern, html_str).group(1)
        except AttributeError:
            if content_list != list():
                # 此時為貼吧的最後一頁,代碼執行完畢
                print(content_list)
                print("所要爬取的貼吧已經全部爬取結束")
                next_url = None
            else:
                # 輸入的貼吧名字不存在,代碼結束
                print("你所要爬取的貼吧不存在,請重新輸入貼吧名字")
                next_url = None
        finally:
            return next_url, content_list

    def get_list_img(self, detail_url, all_img_list):  # 擷取單個貼中的所有圖檔
        detail_html_str = self.parse_url(detail_url)
        img_list = re.findall(self.detail_img_pattern, detail_html_str)
        # 擷取目前頁面所有圖檔的連結
        # img_list = detail_html.xpath("""//img[@class="BDE_Image"]/@src""")
        if img_list is not None:
            all_img_list.extend(img_list)
        # 擷取下一頁連結
        try:
            detail_next_url = re.search(self.detail_next_url_pattern, detail_html_str).group(1)
            print("DETAIL_NEXT_URL", detail_next_url)
        except AttributeError:
            # 子貼爬取完畢
            pass
        else:
            if detail_next_url:
                # 存在,則遞歸擷取,循環直到整個貼的圖檔擷取結束
                detail_next_url = self.part_url + detail_next_url
                return self.get_list_img(detail_next_url, all_img_list)
        return all_img_list

    def save_content_list(self, content_list):
        file_path = self.tieba_name + ".txt"
        with open(file_path, 'a', encoding='utf-8') as f:
            # 保證中文的正常寫入
            for content in content_list:
                # 将清單中擷取的資料按照格式寫入,保證中文輸入,保證縮進
                f.write(json.dumps(content, ensure_ascii=False, indent=4))
                # 優化寫入格式,保證中文的寫入
                f.write("\n")

    def download_img(self, content_list): 
    # 下載下傳所有的圖檔并新建立檔案夾存入圖檔
        for content_dic in content_list:
            try:
                # 建立名為title的檔案夾
                os.mkdir('./{}_image/'.format(self.tieba_name) + content_dic["tie_title"])
            except FileExistsError:
                # 如果存在兩個title檔案夾一樣的情況,(由于部分貼吧重新整理快,會出現這種情況)
                print("{}檔案夾已經存在,是否覆寫或者重建立立一個檔案夾".format(os.getcwd() + '/image/' + content_dic["tie_title"]))
                while True:
                    answer = input("Y:确認覆寫,N:重建立立一個檔案夾")
                    if answer == "Y"or"y":
                        # 重新覆寫,會将最新的圖檔下載下傳進入
                        os.makedirs('./{}_image/'.format(self.tieba_name) + content_dic["tie_title"], exist_ok = True)
                        break
                    elif answer == "N"or"n":
                        # 否則則建立尾不同的檔案夾
                        os.mkdir('./{}_image/'.format(self.tieba_name) + content_dic["tie_title"] + '2')
                        break
                    else:
                        print("請重新輸入")
            img_num = 1
            for img_url in content_dic["img_list"]:  
            # 對content_dic["img_list"]進行周遊,對于每一個img_url下載下傳,編号為1~N
                with open('./{}_image/'.format(self.tieba_name) + content_dic["tie_title"] + '/' + str(img_num) + '.jpg', 'wb') as f:
                    # 二進制檔案,不能用utf-8
                    img_content = requests.get(img_url)
                    f.write(img_content.content)
                    img_num += 1

    def run(self):

        next_url = self.start_url
        try:
            # 建立名為所要爬取貼吧名字的檔案夾,存儲圖檔
            os.mkdir('./{}_image/'.format(self.tieba_name))
        except FileExistsError:
            # 如果已經存在報錯,并且忽略檔案夾時,會對原檔案夾進行寫入
            print("當檔案已存在時,無法建立該檔案。: './lol_image/'", "\n是否繼續進行?")
            input("按下任意鍵繼續")
        # 主循環
        while next_url is not None:
            # 1、擷取第一個url位址,以及生成的html
            html_str = self.parse_url(next_url)
            # 2、根據第一個url傳回的資訊,提取下一頁url和資料
            next_url, content_list = self.get_content_list(html_str)
            # 3 、實作圖檔的下載下傳并儲存
            self.download_img(content_list)
            # 4、儲存資料
            self.save_content_list(content_list)


def main():
    tieba_name = input("輸入要爬取的網站")
    tieba_sipder = TiebaSipder(tieba_name)
    tieba_sipder.run()


if __name__ == '__main__':
    main()
           

coding = utf-8

author = 幻夜

mail = [email protected]

以貼吧的電腦端為爬取對象

由輸入的貼吧名字,爬取該貼吧下所有子貼的url,tiltle以及子貼下所有圖檔(不包含廣告,頭像類)

之前嘗試用xpath爬取,由于bug太多,最後放棄,采用了正規表達式去爬取

本代碼采取擷取一頁的所有資訊再去存儲,下載下傳,是以是50個存儲一次

該代碼進行多次不同貼吧嘗試,沒有報錯,希望廣大網友能夠發現bug并及時聯系我

版權聲明:本文為部落客原創文章,轉載請附上博文連結!