"""
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 " >下一頁></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并及時聯系我
版權聲明:本文為部落客原創文章,轉載請附上博文連結!