爬蟲---實現爬取百度貼吧(海賊王吧)

效果圖:

在這裏插入圖片描述

源代碼:

import urllib.request
import urllib.parse
import re
import json
import csv
import time
import random

# 獲取網頁源代碼
def get_page(url):
    headers = {
        'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    req = urllib.request.Request(url=url,headers=headers)
    response = urllib.request.urlopen(req)
    html = response.read().decode('utf-8')
    return html

# 解析網頁源代碼,提取數據
def parse_page(html):
    titles = re.findall(r'href="/p/\d+" title="(.+?)"',html)
    authods = re.findall(r'title="主題作者: (.+?)"',html)
    nums = re.findall(r'href="/p/(\d+)"',html)
    links = ['http://tieba.baidu.com/p/'+str(num) for num in nums]  #本質也是一個列表
    focus = re.findall(r'title="回覆">(\d+)',html)  #回覆數量
    ctimes = re.findall(r'title="創建時間">(.+?)<',html)
    data = zip(titles,authods,links,focus,ctimes)
    return data

# 打開文件
def openfile(fm,fileName):
    if fm == 'txt':
        return open(fileName+'.txt','w',encoding='utf-8')
    elif fm == 'json':
        return open(fileName+'.json','w',encoding='utf-8')
    elif fm == 'csv':
        return open(fileName+'.csv','w',encoding='utf-8',newline='')
    else:
        return None

# 將數據保存到文件
def save2file(fm,fd,data):
    if fm == 'txt':
        for item in data:
            fd.write('----------------------------------------\n')
            fd.write('title:' + str(item[0]) + '\n')
            fd.write('authod:' + str(item[1]) + '\n')
            fd.write('link:' + str(item[2]) + '\n')
            fd.write('focus:' + str(item[3]) + '\n')
            fd.write('ctime:' + str(item[4]) + '\n')
    if fm == 'json':
        temp = ('title','authod','link','focus','ctime')
        for item in data:
            json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
    if fm == 'csv':
        writer = csv.writer(fd)
        for item in data:
            writer.writerow(item)

# 開始爬取網頁
def crawl():
    kw = input('請輸入主題貼吧名字:')
    base_url = 'http://tieba.baidu.com/f?kw=' + urllib.parse.quote(kw) + '&ie=utf-8&pn={page}' #用此方法提前標記好 page
    fm = input('請輸入文件保存格式(txt、json、csv):')
    while fm!='txt' and fm!='json' and fm!='csv':
        fm = input('輸入錯誤,請重新輸入文件保存格式(txt、json、csv):')
    fd = openfile(fm,kw)
    page = 0
    total_page = int(re.findall(r'共有主題數<span class="red_text">(\d+)</span>個',get_page(base_url.format(page=str(0))))[0])
    print('開始爬取')
    while page < total_page:
        print('正在爬取第', int(page/50+1), '頁.......')
        html = get_page(base_url.format(page=str(page)))
        data = parse_page(html)
        save2file(fm,fd,data)
        page += 50
        time.sleep(random.random())
    fd.close()
    print('結束爬取')

if __name__ == '__main__':
    crawl()

講解:

一、open 方法

這個函數能打開當前目錄下的文件,若沒有該文件則自動創建一個新的文件,方法如下:

open(fileName+'.txt','w',encoding='utf-8')

函數返回值:fd = open(fileName+'.txt','w',encoding='utf-8'),可用於讀寫和關閉文件,方法如下:

fd.write('title:' + str(item[0]) + '\n')  #寫入
fd.close()  #關閉

二、parse 模塊的 quote 方法

urllib.parse.quote(kw)

作用是將具有中文的不合理 URL 處理成合法的 URL(具體方法見源碼)

三、re 模塊

re 也稱正則表達式,是一組特殊的字符序列,常常用於匹配字符串

下面講講 re 模塊中的 findall 方法,以源代碼舉例:

re.findall(r'共有主題數<span class="red_text">(\d+)</span>個',get_page(base_url.format(page=str(0)))[0]

這段看起來很長,我就來分段講解

base_url.format(page=str(0))

這句就是給前面的 page 賦值爲字符串的 0,就是 format 的基本用法

r'共有主題數<span class="red_text">(\d+)</span>個'

其中r'xxxx'(\d+) 就是正則表達式,前者代表引號內爲正則表達式,後者代表整數( \d 代表一個數字,+ 代表取一或多次)
並且 (\d+)\d+ 的意思完全不一樣,有括號代表僅匹配括號內的數字,沒括號代表連着數字的其他字符也一起匹配

re.findall(xxxx,xxxx)[0]

第一個參數代表要匹配內容,第二個參數代表被匹配的字符串(一般是整個 html 內容)
函數返回 匹配成功內容 的列表,[0] 代表取列表第一個元素

再舉一個例子:

titles = re.findall(r'href="/p/\d+" title="(.+?)"',html)

(.+?) 中的 . 號爲匹配除換行符之外的所有字符,? 號爲匹配字符零次或一次

函數返回值:findall 方法 的返回值是一個列表

四、time 模塊 和 random 模塊

time 模塊 的 sleep 方法

time.sleep(t) #進程掛起的時間爲 t 秒

random 模塊 的 random 方法

 random.random() #返回隨機生成的一個實數,它在[0,1)範圍內

五、將數據保存到文件

def save2file(fm,fd,data):  #選擇格式 選擇文件 zip內容
    if fm == 'txt':
        for item in data:
            fd.write('----------------------------------------\n')
            fd.write('title:' + str(item[0]) + '\n')  # zip解壓
            fd.write('authod:' + str(item[1]) + '\n')
            fd.write('link:' + str(item[2]) + '\n')
            fd.write('focus:' + str(item[3]) + '\n')
            fd.write('ctime:' + str(item[4]) + '\n')
    if fm == 'json':
        temp = ('title','authod','link','focus','ctime')
        for item in data:
            json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
    if fm == 'csv':
        writer = csv.writer(fd)
        for item in data:
            writer.writerow(item)

上面代碼可以當作模板來參考使用,具體我也不是很瞭解,用時回顧,不必深究

參考博客:
爬蟲系列(三) urllib的基本使用
爬蟲系列(五) re的基本使用
爬蟲系列(六) 用urllib和re爬取百度貼吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章