首先確定要爬取的笑話網站,我是直接百度笑話搜到的第一個網站,網址是http://www.jokeji.cn/,點進去發現網頁構建在我看來還是比較複雜的,由於還是初學者,首先得先找到網頁資源集中所在,找出其中的規律,然後才好有針對性的爬取資源。對於這個網址,我發現在左邊側欄有一個笑話大全的分類框。這個 ...
首先確定要爬取的笑話網站,我是直接百度笑話搜到的第一個網站,網址是http://www.jokeji.cn/,點進去發現網頁構建在我看來還是比較複雜的,由於還是初學者,首先得先找到網頁資源集中所在,找出其中的規律,然後才好有針對性的爬取資源。對於這個網址,我發現在左邊側欄有一個笑話大全的分類框。這個看起來基本上包括了全站的文字笑話了。在這個分類框下有許多小的分類,點進小的分類後是此分類下的所有笑話網頁列表,每個網頁裡面包含一些笑話,我們最終是要把這一個一個網頁里的笑話爬取存儲起來。
爬取思路:爬取http://www.jokeji.cn/,得到一個html頁面,分析此html頁面,獲得分類框里的所有分類的url地址,遍歷每一個分類的url,爬取分類url的html網頁,分析分類url網頁,得到全部笑話網頁列表,遍歷笑話網頁列表url,得到最終的包含一個一個笑話的html頁面,獲取笑話,存儲到mysql資料庫。
爬取我用的是Google瀏覽器,在這個網站下按F12就可以看到網頁源代碼了,此時要分析這個笑話大全的分類框的結構,以便使用python正則表達式獲取到我們想要的信息。
一般篩選內容都會選擇目標內容組件的上層容易唯一標識的組件,在這裡我選擇了<div class=”joketype l_left”></div>這個html,這個div可以包含所有分類的內容,然後再進行一次篩選就可以把所有url篩選出來了。到了分類子頁面我看了一下url的規律,這個分類的url都是/listXX_1.htm開始,發現分類頁面裡面有個尾頁的按鈕的url剛好是結束,並且url的.htm前面的那個數字遞增,所以就很好爬取所有笑話頁面了,至於提取笑話就不再多說了,直接上所有代碼。
mysql資料庫存儲模塊代碼,文件名為mysql.py。
import pymysql def insert(joke): #獲取鏈接 conn=pymysql.connect(host='127.0.0.1',user='root',passwd='123456',db='python') cur=conn.cursor() #sql語句,%s是占位符(%s是唯一的,不論什麼數據類型都使用%s)防止sql註入 sql='insert into joke(joke) VALUES(%s)' #params=('eric','wuhan') #參數 插入單條 #li=[('a1','b1'),('a2','b2')] #批量插入參數 #reCount=cur.execute(sql,params) reCount=cur.executemany(sql,joke) #批量插入 conn.commit() #提交,執行多條命令只需要commit一次就可以 cur.close() conn.close() def get_one(): #獲取鏈接 conn=pymysql.connect(host='127.0.0.1',user='root',passwd='123456',db='python') cur=conn.cursor() sql1='select number from number' reCount=cur.execute(sql1) number=cur.fetchone()[0]+1 sql2='select joke from joke where id=%s' reCount=cur.execute(sql2,number) joke=cur.fetchone()[0] sql3='update number set number=%s where number=%s' params=(number,number-1) reCount=cur.execute(sql3,params) conn.commit() cur.close() conn.close()
註意:pymysql模塊是需要安裝的,一般現在python都預設有pip包管理,沒有這個模塊直接在命令行執行:pip pymysql即可。需事先在mysql資料庫里創建python資料庫,表joke,表有兩列,一列是id,我設置其為int類型的自增的主鍵屬性,一列joke,設置為text類型,存放笑話。insert函數是傳入一個joke的tuple類型參數,這個tuple裡面存放是一條一條字元串,每條字元串表示一個笑話,insert函數的功能是將這個tuple元組的笑話插入到資料庫。get_one函數是得到一條笑話,在這裡我添加一個表專門來保存笑話取到哪了,以免讀取重覆笑話,這個表名字是number,裡面有一個欄位,number,我實現插入了一條記錄,值為0。
爬蟲代碼,文件名為spider.py。
import urllib from urllib import request import re import chardet import mysql import time ''' 找到http://www.jokeji.cn/list29_1.htm笑話列表 獲取第一個笑話網頁http://www.jokeji.cn/jokehtml/bxnn/2018080417455037.htm 遍歷完所有笑話網頁 獲取下一個笑話列表,重覆遍歷,直至此分類笑話遍歷完 遍歷下一個分類 ''' class Spider(): url='http://www.jokeji.cn/jokehtml/bxnn/2018073023294432.htm' header = { 'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36', 'Accept-Encoding': '', 'Referer':'http://www.jokeji.cn/list29_1.htm' } classifys=[] #list集合,存儲笑話分類url #獲取html文本 def __fetch_content(self): cookie_html = request.Request(Spider.url,headers=Spider.header) cookie_html = request.urlopen(cookie_html) htmls=cookie_html.read() #使用chardet獲取到htmls的編碼格式 encoding=chardet.detect(htmls) encoding=encoding['encoding'] #不加ignore的時候總是出現字元轉換錯誤,說明有非法字元,必須加上ignore忽略非法字元 htmls=htmls.decode(encoding,'ignore') time.sleep(0.1) return htmls #獲取笑話分類url def __analysis_classify(self): Spider.url='http://www.jokeji.cn/' Spider.header['Referer']='http://www.jokeji.cn/' htmls=self.__fetch_content() root_pattern='<div class="joketype l_left">([\s\S]*?)</div>' classify_pattern='<a href="([\s\S]*?)">' root_html=re.findall(root_pattern,htmls) Spider.classifys=re.findall(classify_pattern,str(root_html)) #獲取當前頁面里的所有笑話,返回一個tuple def __analysis(self,htmls): anchors=[] root_pattern='<span id="text110">([\s\S]*?)</span>' juck_pattern='<P>\d、([\s\S]*?)</P>' root_html=re.findall(root_pattern,htmls) for html in root_html: jucks=re.findall(juck_pattern,html) #替換換行符 c=re.compile('<[b|B][r|R]\s*/*>') for i in jucks: i=c.sub('\n',i) anchors.append(i) #i=i.replace('<BR>','\n') return anchors return anchors #爬取原創笑話 def __analysis_yuanchuangxiaohua(self,url): url='http://www.jokeji.cn'+url pass #爬取分類下的笑話 def __analysis_joke(self): Spider.header['Referer']='http://www.jokeji.cn/' root_pattern='<div class="list_title">([\s\S]*?)</div>' page_pattern='<a href="([\s\S]*?)"\s*target="_blank"\s*>' for classify in Spider.classifys: try: if '/yuanchuangxiaohua' in classify: self.__analysis_yuanchuangxiaohua(classify) classify='http://www.jokeji.cn'+classify Spider.url=classify htmls=self.__fetch_content() #記錄分類裡面最大頁面數 max_number=int(re.findall('[\s\S]*?(\d*?)\.htm">尾頁</a>',htmls)[0]) #開始遍歷每一大頁,一大頁包含很多小頁面,小頁面裡面才是笑話 except BaseException: continue for bigpage_number in range(1,max_number+1): try: bigpage_url=classify temp=re.compile('(\d*).htm') #更改url,因為分類下每一大頁都是有規律的,都是.htm前面的數字從1加到最大頁面數 bigpage_url=temp.sub(str(bigpage_number)+'.htm',bigpage_url) #替換url Spider.url=bigpage_url htmls=self.__fetch_content() root_html=re.findall(root_pattern,htmls) #獲取一大頁裡面的小頁面的url地址 pages=re.findall(page_pattern,root_html[0]) #遍歷所有小頁面url,獲取其中的笑話 except BaseException: continue for page in pages: try: Spider.url='http://www.jokeji.cn'+page htmls=self.__fetch_content() tuples=self.__analysis(htmls) #插入到資料庫 mysql.insert(tuples) except BaseException: continue def go(self): self.__analysis_classify() self.__analysis_joke() spider=Spider() spider.go()
由於笑話分類里的原創笑話的頁面格式不同,所以就差這個還沒實現。初學,第一次寫博客記錄,下載了編寫博客的openLiveWriter發現很難用,還是用網頁寫好一點。