1 #coding:utf-8 2 import requests 3 from bs4 import BeautifulSoup 4 import MySQLdb 5 6 7 def get_html(url): 8 ''' 9 獲取頁面HTML源碼,並返回 10 ''' 11 html = requests.get(url) 12 content = html.text.encode('utf-8') 13 return content 14 15 def get_blog_html_list(html_content): 16 global addr 17 ''' 18 在HTML源碼中獲取到blog相關的值,並返回 19 ''' 20 bs = BeautifulSoup(html_content,'lxml') 21 # 此處的class要加上下划線,否則會和系統內預定的class沖突 22 blog_list = bs.find_all('div', class_ = 'entrylistItem') 23 for i in range(len(blog_list)): 24 sub_dict = {} 25 sub_dict['title'] = blog_list[i].a.get_text() 26 sub_dict['link'] = blog_list[i].a.get('href') 27 sub_dict['abstract'] = blog_list[i].div.get_text() 28 insert_to_db(sub_dict) 29 return sub_dict 30 31 def insert_to_db(dict={} ): 32 conn = MySQLdb.connect(host = 'localhost', user = 'root', passwd = '12345a', 33 db = 'pytest01', charset = 'utf8') 34 cur = conn.cursor() 35 36 try: 37 cur.execute('insert into blog_details (title, link, abstract) values (%s, %s, %s)', 38 (dict['title'], dict['link'], dict['abstract'])) 39 except MySQLdb.Error, e: 40 pass 41 conn.commit() 42 cur.close() 43 conn.close()
1 #coding:utf-8 2 import urllib 3 import requests 4 from bs4 import BeautifulSoup 5 import MySQLdb 6 import Crawler.get_file 7 from docx import Document 8 import time 9 import docx.image # 超鏈接圖片拋出的異常在docx.image中 10 11 index = '0' 12 13 def get_blog_pic(pic_url, index): 14 print 'download picture' 15 ''' 16 下載指定URL的圖片,並且以index命名保存到temp_pic 文件夾內 17 ''' 18 path = 'D:\\workspace_Java\\cnBlogCrawler\\temp_pic\\' + index + '.png' 19 try: 20 pic = urllib.urlretrieve(pic_url, path) 21 except AttributeError,e: 22 path = r'C:\Users\Lisen\Desktop\error.jpg' 23 print 'download picture error, the link is ' + pic_url 24 pass 25 finally: 26 return path 27 28 def get_blog_body(url, title): 29 print 'download blog' 30 print title, time.strftime('%Y-%m-%d %H:%M:%S') 31 file_path = 'D:\\workspace_Java\\cnBlogCrawler\\temp_doc\\' + title + '.docx' 32 header = {'Accept':'text/html,application/xhtml+xml,application/xml', 33 'Accept-Encoding':'gzip, deflate, sdch', 34 'Accept-Language':'zh-CN,zh;q=0.8', 35 'Cache-Control':'max-age=0', 36 'Connection':'keep-alive', 37 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36'} 38 html = requests.get(url,header).text 39 bs = BeautifulSoup(html, 'lxml') 40 print 'html ready' 41 ''' 42 獲取html中blog正文部分 43 ''' 44 body = bs.find_all('p') 45 ''' 46 創建文檔 47 ''' 48 doc = Document() 49 for each in body: 50 # 在函數中改變全局變量需要用global聲明下 51 global index 52 ''' 53 如果p節點有子節點a的話,就說明其中是帶着圖片的鏈接或者是文本本身就是鏈接,因此要分三種情況去爬取 54 1. 沒有子節點:直接獲取text部分 55 2. 有子節點 a, 但是該子節點沒有text部分,說明這是圖片 56 3. 有子節點a, 且該子節點有text部分,則里面是文本 57 ''' 58 if each.a != None: 59 txt = each.a.get_text() 60 if txt == '': 61 print 'pic' 62 url = each.a.get('href') 63 index = str(int(index) + 1) 64 pic_name = get_blog_pic(url, index) 65 try: 66 doc.add_picture(pic_name) 67 except docx.image.exceptions.UnrecognizedImageError, e: 68 pass 69 else: 70 print 'URL' 71 content = each.a.get_text() 72 paragraph = doc.add_paragraph(content) 73 else: 74 print 'txt' 75 content = each.get_text() 76 paragraph = doc.add_paragraph(content) 77 try: 78 print 'saving file' 79 doc.save(file_path) 80 except IOError,e: 81 print 'file saving error' 82 print 'error url is ' + url 83 pass
1 import MySQLdb 2 import Crawler.get_file 3 import Crawler.get_blog_details 4 import time 5 6 if __name__ == '__main__': 7 8 conn = MySQLdb.connect(host = 'localhost', user = 'root', passwd = '12345a', 9 db = 'pytest01', charset = 'utf8') 10 cur = conn.cursor() 11 cur.execute('truncate blog_details') 12 url = r'http://www.cnblogs.com/omygod/category/460309.html' 13 print 'next parsing html' 14 html = Crawler.get_blog_details.get_html(url) 15 print 'next parsing blog address' 16 dict = Crawler.get_blog_details.get_blog_html_list(html) 17 print 'next update MySQL' 18 Crawler.get_blog_details.insert_to_db(dict) 19 20 cur.execute('select * from blog_details') 21 lines = cur.fetchall() 22 for line in lines: 23 Crawler.get_file.get_blog_body(line[1],line[0]) 24 print 'done' + time.strftime('%Y-%m-%d %H:%M:%S') 25 time.sleep(2) 26 print 'wait over' 27 cur.close() 28 conn.close()
由於博客園的原創博客都是通過隨筆的形式保存的,因此我們可以通過對某一隨筆目錄進行解析,獲取出該目錄下所有博文的標題,鏈接以及摘要,存儲到MySQL數據庫中(主要是因為可以持久記錄相關信息,后續有新博文的時候可以通過對比判斷直接下載新的博文)。然后再對每個條目進行單獨解析,將博文的內容,圖片保存到Word文檔中。
主要用到的包有: requests, BeautifulSoup,python-docx, MySQLdb
- requests和BeautifulSoup主要是用來獲取網頁源代碼以及對其進行解析;
- python-docx是用來生成Word文檔,手冊可參照: https://python-docx.readthedocs.io/en/latest/user/quickstart.html
- MySQLdb是用來通過Python操控MySQL數據庫, 具體可參照SegmentFault中的一篇文章:https://segmentfault.com/a/1190000000709735
整個代碼分為三部分:
- 第一部分是get_blog_details.py,主要是獲取目錄下各個博文的詳細信息,並存到MySQL數據庫中
- 第二部分是get_file.py,獲取各個博文的地址等並生成Word文檔
- 第三部分是__init__.py, 程序開始
在編寫代碼過程中,有幾個地方需要注意:
- 若在函數中改變全局變量,需要在函數中聲明: global g_var
- 在用BeautifulSoup解析某個節點的class屬性是,不能直接用class否則會報錯,因為class是保留關鍵字, 要在class后加_, 如:blog_list = bs.find_all('div', class_ = 'entrylistItem')
- 在py文件中import其他py文件時,一要在文件目錄中添加 __init__.py 文件,二是要在導入是加上對應文件夾的名字,如:import Crawler.get_file
現階段缺點:
- 程序運行速度慢,后期准備學習下多線程爬蟲
- Word文檔中圖片太大以至於不方便閱讀,准備用python-docx中的Inches修改
- 剛才跑的時候發現寫數據庫表的時候有一個地方沒注意,相同主鍵不能再次寫入,這個需要修改下 現在每次解析博文列表時都會先進行刪除數據庫的操作
- 沒有例外處理,程序容易崩潰 一定要添加例外處理,否則指不定程序在哪里就崩潰了。。
- 在爬取博文時發現有些博文內容含有表格,以及某些圖片是超鏈接形式,表格在下一版中修改,超鏈接圖片在此版中作為異常處理
體會:
1. 真的真的要做異常處理,否則會很痛苦
2. 分析不全面,沒有考慮到含有表格或者其他形式
3. 爭取做出GUI,方便處理
4. 添加log 文檔, 方便查看出錯的地方