最近開始學Python的爬蟲,是在這個博客跟著學習的,該博主用的是Python 2.7版本,而我使用的是3.5版本,很多不兼容的地方,不過沒關系,自己改改就好了。
我們想針對網站的內容進行篩選,只獲取自己感興趣的部分。比如你想在XX網站把小黃圖篩選出來,打包帶走。這里只做簡單的實現,以百思不得姐上的段子(純文本)為例。我們想要實現如下功能:
- 批量下載若干頁段子到本地文件中
- 按下任意一鍵,開始閱讀下一條段子
1. 獲取網頁代碼
導入urllib
的相關庫,Python 3中應該這樣寫:
import urllib.request
import urllib.parse
import re
re庫是正則表達式(Regular Expression),后面作匹配時會用到。
百思不得姐的段子頁面url ='http://www.budejie.com/text/1'
,這里末尾數字1代表此為第一頁。通過以下代碼就能返回網頁內容。
req = urllib.request.Request(url)
# 添加headers 使之看起來像瀏覽器在訪問
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36')
response = urllib.request.urlopen(req)
# 得到網頁內容,注意必須使用decode()解碼
html = response.read().decode('utf-8')
print(html)
的話,就是如下所示的內容:
這能看?段子呢?我們想要的段子呢?!
哦對了headers這樣查看。
按F12,然后...看圖吧
2. 正則匹配提取段子
要想篩選符合普通人閱讀的內容(如果還帶著html標簽那還咋讀是不),成功提取出段子,為此我們需要一些既定的模式去和網頁全部內容進行匹配,將模式下匹配成功的對象返回。我們使用強大的正則表達式進行匹配(Regular Expression),相關語法可以看這里。
僅僅針對本例中的網頁內容,先看看我們需要的段子對應了網頁中的什么內容。
可以看到段子被<div class="j-r-list-c-desc">(我們要的內容)</div>
這樣的標簽所包圍,只需要指定相應規則提取出來即可!上圖可以看出段子正文前后是有很多空格的,需要匹配進去。
pattern = re.compile(r'<div class="j-r-list-c-desc">\s+(.*)\s+</div>')
result = re.findall(pattern, html)
通過
re
庫的compile
函數制定規則。
\s+
可以匹配一個或更多的空格.
匹配除開換行符\n
外的所有字符。
現在我們得到了匹配后的結果,來看下。
Bingo!提取出來了不是?!
可是我們發現里面還有些討厭的<br />
。沒關系,寫幾行代碼的事。這里就不再展示去掉后的內容,自行腦補哈哈。
for each in content:
# 如果某個段子里有<br />
if '<br />' in each:
# 替換成換行符并輸出
new_each = re.sub(r'<br />', '\n', each)
print(new_each)
# 沒有就照常輸出
else:
print(each)
這里
content
由get_content方法返回,表示所有獲取到的段子列表。get_content方法下面會給出實現。
至此,我們成功得到我們想看的段子了!如果想要下載到本地呢?
3. 下載段子到本地
通過定義一個save()
函數即可,num
參數用戶自定,想下載最近100頁的內容都沒問題!里面還有些變量沒有提到,最后會給出源代碼。
# num是指定網頁頁數
def save(num):
# 寫方式打開一個文本,把獲取的段子列表存放進去
with open('a.txt', 'w', encoding='utf-8') as f:
text = get_content(num)
# 和上面去掉<br />類似
for each in text:
if '<br />' in each:
new_each = re.sub(r'<br />', '\n', each)
f.write(new_each)
else:
f.write(str(each) + '\n')
下載到本地文檔后如下圖所示
4. 逐條讀取段子
段子太多,琳瑯滿目。可我們只希望一條條閱讀。通過按下鍵盤任意鍵可以切換到下一條,直到讀取到最后一條程序才結束,或者通過設置一個退出鍵隨時退出程序,比如設定q
鍵退出。這里把全部代碼給出。
import urllib.request
import urllib.parse
import re
pattern = re.compile(r'<div class="j-r-list-c-desc">\s+(.*)\s+</div>')
# 返回指定網頁的內容
def open_url(url):
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36')
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
return html
# num為用戶自定,返回的是所有頁的段子列表
def get_content(num):
# 存放段子的列表
text_list = []
for page in range(1, int(num)):
address = 'http://www.budejie.com/text/' + str(page)
html = open_url(address)
result = re.findall(pattern, html)
# 每一頁的result都是一個列表,將里面的內容加入到text_list
for each in result:
text_list.append(each)
return text_list
# num是指定網頁頁數
def save(num):
# 寫方式打開一個文本,把獲取的段子列表存放進去
with open('a.txt', 'w', encoding='utf-8') as f:
text = get_content(num)
# 和上面去掉<br />類似
for each in text:
if '<br />' in each:
new_each = re.sub(r'<br />', '\n', each)
f.write(new_each)
else:
f.write(str(each) + '\n')
if __name__ == '__main__':
print('閱讀過程中按q隨時退出')
number = int(input('想讀幾頁的內容: '))
content = get_content(number + 1)
for each in content:
if '<br />' in each:
new_each = re.sub(r'<br />', '\n', each)
print(new_each)
else:
print(each)
# 用戶輸入
user_input = input()
# 不區分大小寫的q,輸入則退出
if user_input == 'q' or user_input == 'Q':
break
演示一下,效果是這樣的。
雖然功能很雞肋,不過作為初學我還是很滿意了,有興趣才能深入下去嘛!爬蟲可不僅僅如此而已,以后會學習更加高級的功能。
by @sunhaiyu
2016.8.15