利用pyhton爬虫(案例4)--你想要的图片都在这
學習筆記
爬取X度某吧里的小圖片們
寫個小案例,獲取X度里,你指定的吧里,第kkk頁之前所有帖子內的圖片,當然這個kkk由你定。
爬取步驟
①獲取用戶指定吧名和指定頁數,得到貼吧主頁URL。
②獲取1頁中所有帖子URL地址,并獲取本吧的最大頁數。
③for循環每個帖子的URL地址,對每個帖子鏈接發請求
④獲取一個帖子的最大頁數,遍歷帖子內所有頁里的圖片
⑤拿到圖片的鏈接后,獲取圖片以wb的形式,保存到mysql數據庫。
⑥第1頁處理完后,再處理第2頁,以此類推…直到處理到用戶指定頁面數。
總結一下!我們需要用Xpath獲取貼吧內帖子的url,帖子內所有圖片的url;
并且用正則表達式匹配某個吧內的最大頁數,一個帖子內的最大頁數。
URL特征
- 同一個吧內不同頁面URL地址特征
第二頁:
https://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&ie=utf-8&pn=50第三頁:
https://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&ie=utf-8&pn=100可以看到查詢參數pn在第2頁為50,第3頁為100,則可以推斷此處的pn參數可以控制頁數,且第kkk頁pn=(k?1)?50pn=(k-1)*50pn=(k?1)?50。
同時,我們判斷kw參數后是吧名的ASCII編碼值,我們將其轉換為中文字符,驗證一下:
嗯!是的呢~
- 同一個帖子內不同頁面URL特征
第二頁:
https://tieba.baidu.com/p/6554079192?pn=2第三頁:
https://tieba.baidu.com/p/6554079192?pn=3可以看到查詢參數pn在第2頁為2,第3頁為3,則可以推斷此處的pn參數可以控制頁數,且第kkk頁pn=kpn=kpn=k
Xpath表達式
在這里我就不放頁面源代碼進行HTML頁面分析了,因為寫這個Blog的時候有點晚了,不想貼太多圖片了,腦子也轉不動了,而且也有某個紅領巾幫我寫好Xpath了。
這個紅領巾是誰呢?我們右鍵點擊網頁,選擇【審查元素】–>選中要獲取的頁面信息,再右鍵選擇【Copy】–>最后選擇【Copy Xpath】! 這樣就得到Xpath表達式啦!
O_o…wc, 要是那么簡單就可以獲取Xpath表達式了,我之前還花了1天時間寫了一系列Xpath總結???心碎…
比如,我想獲取如下帖子的URL地址:
通過Copy Xpath,看一看這個紅領巾幫我們獲取的Xpath:
//*[@id="thread_list"]/li[1]/div/div[2]/div[1]/div[1]/a在用Chrome插件Xpath Helper檢查一下,看看匹配到了啥:
可以看到只匹配到了1個結果,而且還不是帖子鏈接,只是帖子文本!但是我們想要的是匹配50個帖子鏈接,所以,這個紅領巾給的Xpath表達式"不怎么合格"。雖然這個表達式不怎么合格,但是它給了我們一些提示,我們可以按照紅領巾給我們的Xpath表達式邏輯,來寫出符合要求的合格表達式。
這里就不具體寫咋獲取合格Xpath表達式的具體步驟了。
直接放修改后的Xpath表達式(注意,Xpath表達式中最好不要帶位置謂詞,但是可以寫帶屬性的謂詞):
//*[@id="thread_list"]/li//div[@class="t_con cleafix"]/div/div/div/a/@href在Xpath Helper中檢查一下:
perfect! (需要注意的是,這個Xpath表達式,是參考別人的,我自己寫了一個表達式,用Xpath Helper可以匹配到URL,但是在python中就匹配不到了,心累!不知道為啥,這里標記一下,以后深入學習后,再回來解決)
備注:不要過度加謂詞(條件),這樣可能會導致一些數據的丟失。
圖片鏈接的URL我們也可以試著用這種方式獲取,這里也不詳述了,直接給Xpath表達式:
//img[@class="BDE_Image"]/@src正則表達式
貼吧最大頁數正則表達式:
下一頁.*?<a href=.*?&pn=(\d*?)".*?>尾頁</a>帖子內頁面總數正則表達式:
<li class="l_reply_num".*?回復貼,共.*?<span class="red">(\d*?)</span>頁</li>mysql內創建存儲圖片數據的數據庫
在敲python代碼之前,我們先建一個mysql數據庫,等下爬蟲時,存放我們的圖片信息。
use datacup; create table ba_image_table(id int primary key auto_increment, ba_name varchar(30) not null, image_url varchar(200) default "None", image_data mediumblob);python代碼與mysql操作
注意,下面代碼中的my_user_agent_list是我的自定義模塊,里面是專門放User-Agent的列表。
代碼:
# -*- coding: utf-8 -*-import requests from lxml import etree import pymysql import random import time from urllib import parse from my_user_agent_list import user_agent import math import reclass PagenumError(Exception):def __init__(self, msg, page_value):super().__init__(msg)self.page_value = page_valueclass BaNameError(Exception):def __init__(self, msg):super().__init__(msg)class TiebaSpider:def __init__(self):self.url = 'http://tieba.baidu.com/f?kw={}&pn={}'self.user_agent = user_agentself.page = 1self.db = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',password = '19970928',database = 'datacup',charset = 'utf8')self.cur = self.db.cursor()#獲取html頁面def get_page(self, url):try:res = requests.get(url, headers = {'User-Agent':random.choice(self.user_agent)})test_url = res.urlprint(test_url)if not self.url_exist(test_url):raise BaNameError('請檢查輸入貼吧是否存在是否正確...') #print(res.url)except BaNameError as e:print(e.args)http_status_code = -1html = 'None'except Exception as e:print('連接錯誤,未知錯誤')http_status_code = -1html = 'None'else:html = res.content.decode('utf-8')#print(html)http_status_code = res.status_codeprint(http_status_code)return (http_status_code, html)#解析HTML頁面,拿到該頁面所有帖子鏈接和全部圖片def parse_page(self, html):parse_html = etree.HTML(html)li_xpath = '//*[@id="thread_list"]/li//div[@class="t_con cleafix"]/div/div/div/a/@href'#拿鏈接,去廣告li_list = parse_html.xpath(li_xpath)#print('本頁帖子數:', len(li_list))li_list = ["http://tieba.baidu.com"+item for item in li_list if re.findall(r'^/p', item)]print('本頁帖子數:', len(li_list))image_matrtix = []for item in li_list:image_list = self.parse_two_page(item)image_matrtix.append(image_list)return sum(image_matrtix, [])#獲取帖子內的圖片URLdef parse_two_page(self, url):(http_status_code, html) = self.get_page(url)page_num = self.get_page_num(html, url)image_xpath = '//img[@class="BDE_Image"]/@src'image_list = []for item in range(1, page_num + 1):temp_url = url + "/?pn={}".format(item)print("帖子url:", temp_url)(http_status_code, html) = self.get_page(temp_url)parse_html = etree.HTML(html)image = parse_html.xpath(image_xpath)print('本頁圖片數:', len(image))image_list.append(image)return sum(image_list, [])#圖片下載def downloads_image(self, url_list, ba_name):data_list = []for url in url_list:image_data = requests.get(url, headers = {'User-Agent':random.choice(self.user_agent)}).contentdata_list.append((ba_name, url, image_data))self.write_page(data_list) #寫出數據def write_page(self, data_list):sql = 'insert into ba_image_table(ba_name, image_url, image_data) \values(%s, %s, %s);'try:self.cur.executemany(sql, data_list)self.db.commit()except Exception as e:self.db.rollback()print('錯誤信息:', e)#判斷吧是否存在def url_exist(self, url):#若不存在該吧,則返回Falseif r'search/res' in url:idcard = Falseelse:idcard = Truereturn idcard#頁面控制def get_page_num(self, html, url):if r'f?kw' in url:regex01 = r'下一頁.*?<a href=.*?&pn=(\d*?)".*?>尾頁</a>'pattern = re.compile(regex01, re.S)page_list = pattern.findall(html)print('情況1:', page_list)if not page_list:page_max = 1else:temp_num = int((int(page_list[0])/50) + 1) page_max = temp_numelse:regex02 = r'<li class="l_reply_num".*?回復貼,共.*?<span class="red">(\d*?)</span>頁</li>'pattern = re.compile(regex02, re.S)page_list = pattern.findall(html)print('情況2:',page_list)if not page_list:page_max = 1else:page_max = int(page_list[0])print('本吧目前有{}頁'.format(page_max))return page_max#主函數def main(self):while True:this_page = 1ba_name = input('輸入要爬取的吧名(比如:兔子,末尾不用加"吧"字):')ba_name = parse.quote(ba_name)url = self.url.format(ba_name, 0)print(url)(http_status_code, html) = self.get_page(url)if http_status_code == -1:qtx = input('是否退出(q),不退出則再查詢一次(c):')if qtx == 'q':breakelse:continuecount_inpute_page = 0while True:count_inpute_page +=1try:my_page = math.ceil(int(input('輸入查詢頁數:'))) if my_page <= 0:raise PagenumError('為非正數', my_page)except ValueError as e1:print('輸入的不是數值')except PagenumError as e2:print('錯誤信息:', e2.page_value, e2.args[0])except Exception as e:print('未知錯誤...')print(e.args)else:breakif count_inpute_page >= 3:qtx = input('是否退出,若退出,只查詢第1頁信息(q):')if qtx == 'q':my_page = 1breakurl = self.url.format(ba_name, 0)page_num = self.get_page_num(html, url)in_ba_name = parse.unquote(ba_name)if my_page <= page_num:page_num = my_pagefor page in range(1, page_num + 1):page = (page-1)*50url = self.url.format(ba_name,page)print(url)(http_status_code, html) = self.get_page(url)print(http_status_code)image_url_list = self.parse_page(html)self.downloads_image(image_url_list, in_ba_name)print('共爬取%d次' % self.page)print('本次爬取%d頁' % this_page)self.page += 1this_page += 1time.sleep(random.randint(1, 3))qtx = input('本次查詢完成,是否退出(q),不退出則再查詢一次(c):')if qtx == 'q':breakself.cur.close()self.db.close()if __name__ == '__main__':start = time.time()spider = TiebaSpider()spider.main()end = time.time()print('執行時間:%.2f' % (end-start))控制臺輸出(部分):
入要爬取的吧名(比如:兔子,末尾不用加"吧"字):兔子 http://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&pn=0 https://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&pn=0 200輸入查詢頁數:1 情況1: ['416050'] 本吧目前有8322頁 http://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&pn=0 https://tieba.baidu.com/f?kw=%E5%85%94%E5%AD%90&pn=0 200 200 本頁帖子數: 50 本頁帖子數(排除后): 50 https://tieba.baidu.com/p/6538986625?red_tag=2049280356 200 情況2: ['1', '1'] 本吧目前有1頁 帖子url: http://tieba.baidu.com/p/6538986625/?pn=1 https://tieba.baidu.com/p/6538986625/?pn=1&red_tag=1684499831 200 本頁圖片數: 8 https://tieba.baidu.com/p/6549848737?red_tag=2051121165 200Mysql圖片導出
我們在Mysql Workbench看一下,利用python爬蟲的結果是否導入了數據庫中。
sql執行指令及過程:
16:42:08 select * from ba_image_table LIMIT 0, 1000 533 row(s) returned 0.015 sec / 4.110 sec數據表顯示(部分):
可以看到數據已經導入了,第一個字段為id號,第二個為吧名,第三個為圖片地址,第四個為圖片的二進制形式數據。
現在我們利用python讀取一個圖片數據,并顯示出來。
python代碼:
# -*- coding: utf-8 -*-import pymysqldb = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',password = '19970928',database = 'datacup',charset = 'utf8') cur = db.cursor()sql = "select image_url,image_data from ba_image_table where id=820;" cur.execute(sql) (url, data) = cur.fetchone() print(url) with open('test/Bunny_image_0408.jpg', 'wb') as f:f.write(data)cur.close() db.close()控制臺輸出:
http://tiebapic.baidu.com/forum/w%3D580/sign=26386e88e5d3572c66e29cd4ba126352/f97fd7ca7bcb0a46def6b2587c63f6246a60af03.jpg爬取到的圖片:
嗯!不錯,這個案例就算做完啦。
后記:雖然說好復試前不寫爬蟲的,但忍不住又寫了。雖然爬到了結果,但是還需要后續進一步學習。
這里有幾個問題:
問題1:有的時候程序會爬不下來,等一會再執行又可以爬下來了
問題2:最大頁數通過正則匹配,因為我通過Xpath沒匹配到…蒼天啊,這是為啥
現在暫時不知道咋解決,這里標記一下,有待修改!
總結
以上是生活随笔為你收集整理的利用pyhton爬虫(案例4)--你想要的图片都在这的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 26款 流媒体服务器开源软件「建议收藏」
- 下一篇: 如何在电脑的右键菜单中添加coolRAR