爬蟲系列(六) 用urllib和re爬取百度貼吧

這篇文章咱們將使用 urllib 和 re 模塊爬取百度貼吧,並使用三種文件格式存儲數據,下面先貼上最終的效果圖html

一、網頁分析

(1)準備工做

首先咱們使用 Chrome 瀏覽器打開 百度貼吧,在輸入欄中輸入關鍵字進行搜索,這裏示例爲 「計算機吧」python

(2)分析 URL 規律

接下來咱們開始分析網站的 URL 規律,以便於經過構造 URL 獲取網站中全部網頁的內容正則表達式

第一頁:http://tieba.baidu.com/f?kw=%E8%AE%A1%E7%AE%97%E6%9C%BA&ie=utf-8&pn=0
第二頁:http://tieba.baidu.com/f?kw=%E8%AE%A1%E7%AE%97%E6%9C%BA&ie=utf-8&pn=50
第三頁:http://tieba.baidu.com/f?kw=%E8%AE%A1%E7%AE%97%E6%9C%BA&ie=utf-8&pn=100
...json

經過觀察不難發現,它的 URL 十分有規律,主要的請求參數分析以下:瀏覽器

  • kw:搜索的關鍵字,使用 URL 編碼,能夠經過 urllib.parse.quote() 方法實現
  • ie:字符編碼的格式,其值爲 utf-8
  • pn:當前頁面的頁碼,而且以 50 爲步幅增加

因此完整的 URL 能夠泛化以下:http://tieba.baidu.com/f?kw={keyword}&ie=utf-8&pn={page}網絡

核心代碼以下:dom

import urllib.request
import urllib.parse
# 獲取網頁源代碼
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

(3)分析內容規律

接下來咱們直接使用快捷鍵 Ctrl+U 打開網頁的源代碼,認真分析每一頁中咱們須要抓取的數據網站

容易發現每個帖子的內容都被包含在一個 <li> 標籤中,咱們可使用正則表達式進行匹配,具體包括:編碼

  • 主題名稱:r'href="/p/\d+" title="(.+?)"'url

  • 主題做者:r'title="主題做者: (.+?)"'
  • 連接地址:r'href="/p/(\d+)"'
  • 回覆數:r'title="回覆">(\d+)<'
  • 建立日期:r'title="建立時間">(.+?)<'

核心代碼以下:

import re
# 解析網頁源代碼,提取數據
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

(4)保存數據

下面將數據保存爲 txt 文件、json 文件和 csv 文件

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

# 將數據保存到文件
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)

二、編碼實現

完整代碼以下,也很簡單,還不到 100 行

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}'
    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()

【爬蟲系列相關文章】

相關文章
相關標籤/搜索