Python爬蟲學習,實戰一糗事百科(2017/7/21更新)

Python爬蟲學習,實戰一糗事百科(2017/7/21更新)
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

前言

這幾天學習爬蟲,網上看了一些教程,發現這個 http://cuiqingcai.com/990.html 是相當不錯的。
但可惜的是,整個教程是兩年前的,但是Python是2.x版本的,跟現在的3.x有一些基本的語法不同;還有糗事百科也經過了改版。
總之原來的爬蟲程式已經無法執行了。
藉此學習機會,我更新一下這篇文章。

目標程序

  1. 本身初學python,暫時用著python2,完成這次爬蟲實驗
  2. 文章順序按照原文章進行
  3. 分析原始碼構成,並進行修改以適應現在的糗事
  4. 最後改成Python3(未完成)

1.確定URL並頁面程式碼

現在的糗百URL為https://www.qiushibaike.com(熱門板塊) ,當然你可以進去到https://www.qiushibaike.com/hot/(24小時板塊)

這裡寫圖片描述


我們還是以熱門板塊來做,刷了幾頁過後發現,他是這樣的:   https://www.qiushibaike.com/8hr/page/5/?s=5001478,後面一堆玩意兒看不懂,
不過試了試https://www.qiushibaike.com/8hr/page/4,也是沒問題,那麼我們的URL就可以出來了

結合原文中的程式碼,我們這麼寫:

# -*- coding:utf-8 -*-
import urllib
import urllib2
page = 1
url = 'https://www.qiushibaike.com/8hr/page/'   str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
print response.read()
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason

2.關於headers驗證

這個headers是用來判斷網站訪問這是否是通過瀏覽器訪問的。
網上關於怎麼查詢headers很齊全。

這裡以chrome為例:

  1. 在網頁任意地方右擊選擇審查元素或者按下 shift ctrl c開啟chrome自帶的除錯工具;
  2. 選擇network標籤,重新整理網頁(在開啟除錯工具的情況下重新整理);
  3. 重新整理後在左邊查詢該網頁url(網址),點選
  4. 後右邊選擇headers,就可以看到當前網頁的http頭了;
  5. 我們用的自然是Request Headers

3.解碼,正規表示式分析

解碼:

利用2的程式碼我們可以抓取到網頁的程式碼,但看上去似乎是一堆亂碼,這個時候我們只需要將原始的讀取稍微轉化一下
response.read()變成response.read().decode('utf-8')

正規表示式

我們先來看原始碼和原頁面
content = response.read().decode('utf-8')
pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?' 
'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,content)
for item in items:
print item[0],item[1],item[2],item[3],item[4]

現在正規表示式在這裡稍作說明

1).*?
是一個固定的搭配,.和*代表可以匹配任意無限多個字元,加上?表示使用非貪婪模式進行匹配,也就是我們會儘可能短地做匹配,以後我們還會大量用到
.*? 的搭配。

2)(.?)代表一個分組,在這個正規表示式中我們匹配了五個分組,在後面的遍歷item中,item[0]就代表第一個(.?)所指代的內容,item[1]就代表第二個(.*?)所指代的內容,以此類推。

3)re.S 標誌代表在匹配時為點任意匹配模式,點 . 也可以代表換行符。

這組樣例沒有圖片:
這裡寫圖片描述

分析(不考慮” 換行的問題)

pattern = re.compile(
'<div.*?author">.*?
<a.*?
<img.*?>(.*?)</a>.*?//釋出人
<div.*?' 'content">(.*?)//內容
<!--(.*?)-->.*?//時間
</div>(.*?)<div class="stats.*?//圖片內容
class="number">(.*?)</i>'#點贊數
,re.S)

糗百改版:

這裡寫圖片描述

  1. 釋出人:

    1. 從div class=”author”到div class=”author clearfix”
    2. 原來只有一個釋出人的href,img後緊跟名字; 現在有兩個,名字在第二個中的正文裡
  2. 內容:沒有改變

  3. 時間:已經刪去此功能

  4. 圖片:沒有修改,在內容和點贊之間的thumb裡

  5. 點贊數:沒有修改

修改完成後的總程式碼是和效果圖

import urllib
import urllib2
import re
page = 1
url = 'http://www.qiushibaike.com/hot/page/'   str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
content = response.read().decode('utf-8')
pattern = re.compile('<div.*?author clearfix">.*?</a>.*?>(.*?)</a>.*?' 
'<div.*?content">.*?<span>(.*?)</span>(.*?)' 
'<div class="stats.*?class="number">(.*?)</i>',re.S)       
items = re.findall(pattern,content)
for item in items:
haveImg = re.search("img",item[2])
if not haveImg:
print "釋出人: ",item[0],"內容:",item[1],"\n點贊數:",item[3] '\n'
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason

這裡寫圖片描述

4.完善互動,設計物件導向模式

照著原始碼,稍微改改吧

import urllib
import urllib2
import re
import thread
import time
class QSBK:
#初始化方法,定義一些變數
def __init__(self):
self.pageIndex = 1
self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
#初始化headers
self.headers = { 'User-Agent' : self.user_agent }
#存放段子的變數,每一個元素是每一頁的段子們
self.stories = []
#存放程式是否繼續執行的變數
self.enable = False
#傳入某一頁的索引獲得頁面程式碼
def getPage(self,pageIndex):
try:
url = 'https://www.qiushibaike.com/8hr/page/'   str(pageIndex)
#構建請求的request
request = urllib2.Request(url,headers = self.headers)
#利用urlopen獲取頁面程式碼
response = urllib2.urlopen(request)
#將頁面轉化為UTF-8編碼
pageCode = response.read().decode('utf-8')
return pageCode
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"連線糗事百科失敗,錯誤原因",e.reason
return None
#傳入某一頁程式碼,返回本頁不帶圖片的段子列表
def getPageItems(self,pageIndex):
pageCode = self.getPage(pageIndex)
if not pageCode:
print "頁面載入失敗...."
return None
pattern = re.compile('<div.*?author clearfix">.*?</a>.*?>(.*?)</a>.*?' 
'<div.*?content">.*?<span>(.*?)</span>(.*?)' 
'<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,pageCode)
#用來儲存每頁的段子們
pageStories = []
#遍歷正規表示式匹配的資訊
for item in items:
#是否含有圖片
haveImg = re.search("img",item[3])
#如果不含有圖片,把它加入list中
if not haveImg:
replaceBR = re.compile('<br/>')
text = re.sub(replaceBR,"\n",item[1])
#item[0]是一個段子的釋出者,item[1]是內容,item[3]是點贊數
pageStories.append([item[0].strip(),text.strip(),item[3].strip()])
return pageStories
#載入並提取頁面的內容,加入到列表中
def loadPage(self):
#如果當前未看的頁數少於2頁,則載入新一頁
if self.enable == True:
if len(self.stories) < 2:
#獲取新一頁
pageStories = self.getPageItems(self.pageIndex)
#將該頁的段子存放到全域性list中
if pageStories:
self.stories.append(pageStories)
#獲取完之後頁碼索引加一,表示下次讀取下一頁
self.pageIndex  = 1
#呼叫該方法,每次敲回車列印輸出一個段子
def getOneStory(self,pageStories,page):
#遍歷一頁的段子
for story in pageStories:
#等待使用者輸入
input = raw_input()
#每當輸入回車一次,判斷一下是否要載入新頁面
self.loadPage()
#如果輸入Q則程式結束
if input == "Q":
self.enable = False
return
print u"第%d頁\t釋出人:%s\t贊:%s\n%s\n" %(page,story[0],story[2],story[1])
#開始方法
def start(self):
print u"正在讀取糗事百科,按回車檢視新段子,Q退出"
#使變數為True,程式可以正常執行
self.enable = True
#先載入一頁內容
self.loadPage()
#區域性變數,控制當前讀到了第幾頁
nowPage = 0
while self.enable:
if len(self.stories)>0:
#從全域性list中獲取一頁的段子
pageStories = self.stories[0]
#當前讀到的頁數加一
nowPage  = 1
#將全域性list中第一個元素刪除,因為已經取出
del self.stories[0]
#輸出該頁的段子
self.getOneStory(pageStories,nowPage)
spider = QSBK()
spider.start()

相關文章

程式語言 最新文章