爬虫爬取知乎评论并利用flask框架做简单的可视化
學完一點簡單的爬蟲技術后,直接開始實踐…
將知乎的某個評論內容爬取下來,取出里面的關鍵字,并按照點贊數排序,形成一個表單,點擊查看,可以看到原來的內容,比如下面這個網頁:
python能做那些有趣還很酷的事
我們發現右邊的下拉條是拉不到底的,而且打開開發者模式,發現拉一點,他就加載一點,我們需要循環拉到底
,然后獲取整個網頁的HTML內容
在進行代碼編譯前需要根據自己瀏覽器的版本,比如我的谷歌在搜索框輸入:chrome://version/
出現:
Google Chrome 96.0.4664.93 (正式版本) (64 位) (cohort: Stable)
我的就是96的
下載地址:http://chromedriver.storage.googleapis.com/index.html
選擇符合自己的版本和系統的壓縮包,解壓后放到項目文件目錄下
第一步:獲取網頁HTML內容
需要導入的庫:
代碼
# 獲取網頁HTML內容 def gethtml(url):driver = webdriver.Chrome() # 初始化一個瀏覽器driver.maximize_window() # 設置窗口最大化driver.get(url) # 打開網頁driver.refresh() # 刷新一下(如果不刷新,在下面循環的時候到最底下就會直接跳出)temp_h = 0 # 設置一個高度為 0js = "var q=document.documentElement.scrollTop=100000"driver.execute_script(js) # 執行上一行的js語句,直接將滾動條下拉到最底下sleep(3) # 等待三秒,向遠程count = 100while count > 0: # 這里設置的循環一百次,想爬取完全可以改為Truecount -= 1driver.execute_script("window.scrollBy(0,4000)") # 循環向下拉去4000個單位,可以按照自己的速度設置快慢sleep(3)check_h = driver.execute_script("return document.documentElement.scrollTop;") # 獲取當前滑動條的位置if check_h == temp_h:sleep(3) # 如果相等,等待3秒網速加載check_h = driver.execute_script("return document.documentElement.scrollTop;")if check_h == temp_h:break # 如果還相等,說明滑動條已經跳到下,評論全部加載完成,跳出循環temp_h = check_h # 將獲取的高度設置為初始高度html = BeautifulSoup(driver.page_source, features="html.parser") # 使用解析器,解析獲取的HTML內容driver.close() # 關閉瀏覽器return html第二步:解析內容
需要導入的庫:
代碼:
# 解析網頁數據 def getData(baseurl, headers):findAgree = re.compile(r'<button class="Button Button--plain" type="button">(.*?) 人贊同了該回答</button>') # 查找點贊人數的正則表達式findContent = re.compile(r'<p data-pid=".*?">(.*?)</p>|<img class="origin_image zh-lightbox-thumb lazy" data-actualsrc="(.*?)"', re.S)# 查找item文本內容和圖片,里面的超鏈接,代碼,和列表等內容也可以創建正則表達式篩選出來,我就沒寫了data = [] # 用來存所有解析好的數據html = gethtml(baseurl) # 調用獲取上一步獲取html代碼的函數if os.path.exists("image"):shutil.rmtree("image")os.mkdir("image") # 創建一個image文件夾,存爬取的圖片,如果已經存在,就刪除for item in html.find_all('div', class_="List-item"): # 解析每一條,div里面class="List-item"的數據datalist = [] # 用來存每一條解析完的數據item = str(item) # 將item改為字符串格式agree = re.findall(findAgree, item) # 點贊人數更改格式if not agree: # 如果點贊人數列表為空就跳出(那是因為我爬取了幾次發現最后一次的點贊人數都為空,會報錯)breaktemp = agree[0].replace(",", "") # 去掉數字里面的","if int(temp) < 5:continue # 點贊數小于5 表示不是我們感興趣的數據content = re.findall(findContent, item) # 用正則表達式查找所有內容stxt = "" # 用來存查找關鍵字的文本image = [] # 用來存需要爬取的圖片的urlnewcontent = "" # 用來存添加了HTML標簽、最后可以直接展示出來的內容for i, j in content:if i == '':if j == "": # 有可能圖片也會存在為空的情況,要排除continueimage.append(j) # 如果content里面的第一項為空,那就說明這次是取到的圖片url,具體content的樣式自己可以輸出來看一看,就明白了newcontent += '<img src="static/image/' + j[26:57] + '.jpg"/><br/>' # 給本地的圖片 添加HTML標簽:src路徑 + 在本地保存的圖片名else: # 下面就是content里面取到的文本內容if i.find("<a class=") == -1: # 查找超鏈接標簽,沒找到i = re.sub(r'<img alt=.*?/>', '', i) # 去掉知乎的樣式圖片stxt += i # 將取出來的文本加入stxt中,用來取出關鍵字i = i + '<br/>' # 加上換行標簽else: # 找到了超鏈接標簽i = re.sub(r'<span><a class=.*?target="_blank">', '', i)i = re.sub(r'<svg class=.*?</a></span>', '', i)i = re.sub(r'<a class=.*?</a>', '', i) # 去掉所有超鏈接內容i = re.sub(r'<img alt=.*?/>', '', i)stxt += ii = i + '<br/>'newcontent += i # 將取到的內容放到新的內容中kword = findkword(stxt) # 查找關鍵字if kword == 0: # 返回0 不存儲continuedatalist.append(temp) # 1、存點贊數datalist.append(kword) # 2、存關鍵字datalist.append(newcontent) # 3、存總文本print("爬取圖片中...") # 下載圖片for i in image:path = "image/" + i[26:57] + ".jpg" # 下載到本地的路徑while True:try:req = requests.get(i, headers=headers, stream=True, timeout=3) # 向圖片的url請求break # 一直死循環爬取,爬取不到不出循環,并不是每次都能爬取成功except requests.exceptions.RequestException as e:continuewith open(path, "wb") as f: # 打開文件,保存圖片到本地f.write(req.content)print("爬取圖片完畢")data.append(datalist) # 添加到需要返回的列表中return data查找關鍵字的函數:
# 查找關鍵字 def findkword(stxt):excludes = ["用戶", "可以", "我們", "這個", "一個", "于是", "大家", "這些", "--------", "-------------------", "---------", "span", "div", "class", "id"]# 一些不屬于我們需要的內容的詞kw = jieba.cut(stxt) # jieba拆分字符串d = {} # 空字典,用來存關鍵字和關鍵字出現的次數for i in kw:if len(i) == 1 or i in excludes: # 去掉我們不需要的詞continued[i] = d.get(i, 0) + 1 # 關鍵字次數加一newd = sorted(d.items(), key=lambda x: x[1], reverse=True) # 按出現的次數排序count = 10 # 這里我們取出現次數最多的前十個temp = ""if len(newd) < count: # 如果關鍵字小于我們需要的關鍵字個數,說明就不是我們想要的數據,就結束,放棄儲存return 0for i in newd:temp += i[0] # 將關鍵字加到我們返回的temp中if count == 0:breaktemp += "、" # 關鍵字之間用"、"分割count -= 1return temp第三步:將得到的數據保存在excel中
導入庫:
代碼:
def saveData(data, savepath):if os.path.exists(savepath):os.remove(savepath) # 判斷excel的路徑是否存在,存在就刪除print("保存數據到excel")book = xlwt.Workbook(encoding="utf-8") # 初始化一個excel對象,編碼格式為utf-8sheet = book.add_sheet("知乎評論內容", cell_overwrite_ok=True) # 向對象中添加一張sheet,更新是覆蓋以前單元的數據col = ("點贊數", "關鍵字", "內容")for i in range(0, 3):sheet.write(0, i, col[i]) # 先寫入一行 標題for i in range(len(data)):for j in range(len(data[i])):sheet.write(i+1, j, data[i][j]) # 依次每個位置寫入數據book.save(savepath) # 保存excel第四步:保存在數據庫中
導入庫:
代碼:
#保存數據到數據庫 def saveDataDB(data, savepathdb):if os.path.exists(savepathdb):os.remove(savepathdb) # 判斷數據庫是否存在,存在就刪除init_db(savepathdb) # 創建數據庫和數據表print("保存數據到數據庫")conn = sqlite3.connect(savepathdb) # 連接到數據庫cur = conn.cursor() # 獲取數據庫游標for d in data:d[2] = d[2].replace("'", "''") # 將數據中的一個單引號變為兩個單引號,SQLITE數據庫的單引號轉義方式for i in range(len(d)):d[i] = "'"+d[i]+"'" # 給每個數據兩邊加上單引號,方便執行sql語句插入sql = '''insert into python(agree,keyword,content)values(%s)'''%",".join(d) # 插入數據,使用","將d鏈表里面的數據鏈接起來cur.execute(sql)conn.commit() # 如果不執行這個語句,數據的增加修改不會真的提交到數據庫cur.close() # 關閉游標conn.close() # 關閉連接創建數據庫:
#創建數據庫 def init_db(savepathdb):sql = '''create table python(id integer primary key autoincrement,agree numeric ,keyword text, content text);''' # 創建表的sql語句conn = sqlite3.connect(savepathdb) # 連接數據庫,如果數據庫不存在則創建一個數據庫cursor = conn.cursor()cursor.execute(sql) conn.commit()conn.close()第五步:主函數調用
def main():baseurl = "https://www.zhihu.com/question/27621722" # 網頁urlheaders = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"} # 添加headers頭,爬取圖片的時候使用data = getData(baseurl, headers) # 得到數據savepath = "有趣的python.xls" # excel路徑savepathdb = "python.db" # 數據庫路徑saveData(data, savepath) # 保存數據到excel表saveDataDB(data, savepathdb) # 保存數據到數據庫print("爬取成功")數據的爬取與存儲就搞定了
excel:
數據庫:
接下來我們做了一點數據的可視化:使用flask框架
第一步:創建主頁
導入庫
主要代碼:
@app.route('/') def index():conn = sqlite3.connect("python.db") # 連接數據庫cur = conn.cursor()sql = "select id,agree,keyword from python order by agree desc" data = cur.execute(sql) # 查找數據并點贊數排序newdate = []count = 0for i in data:count += 1 newdate.append(i+(count,)) # 添加一個名次return render_template("index.html", datalist=newdate) # 跳轉到index.html界面,傳遞一個datalist參數index.html代碼:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>有趣的python</title> </head> <body> <table border="1"><tr><td>排名</td><td>關鍵字</td><td>點贊數</td><td>操作</td></tr>{% for data in datalist %}<tr><td>{{ data[3] }}</td><td>{{ data[2] }}</td><td>{{ data[1] }}</td><td><a href="/look?id={{ data[0] }}">查看</a></td> </tr>{% endfor %} </table> </body> </html>運行完效果:
接著查看頁面:
look.html:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>查看</title> </head> <body> <p>{{ content | safe }}</p> # 將傳過來的文本轉義后輸出 </body> </html>效果:
項目到此還沒結束
我們在index界面添加一個url的輸入框
index.html添加以下代碼:
再將爬取數據的代碼放入flask框架中,用main函數調用一下
將圖片保存的路徑修改一下
這樣我們的項目就已經完成了,可以實現爬取任何知乎網頁數據,只需要你提交一個知乎url
總結:
不會的要學會查閱資料,總有解決辦法。我只爬取了文本和圖片,超鏈接和代碼也可以根據HTML進行正則表達式摘取,還可以判斷是否是知乎網址,還有數據分析,可以制作詞云等比較直觀的方式,頁面也可以美化,但沒必要。雖然代碼短,但是花費的時間一點都不短。才學疏淺,有什么問題或者能改的可以求大佬給點建議。
總結
以上是生活随笔為你收集整理的爬虫爬取知乎评论并利用flask框架做简单的可视化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何快速调整参考文献格式
- 下一篇: kotlin-android-exten