1. 程式人生 > >Python爬蟲初步個人學習及心得

Python爬蟲初步個人學習及心得

自從畢設開始搞了Python之後就覺得這個東西值得研究。

但是畢設的東西非常的淺顯,個人覺得最值得訓練的還是Python的爬蟲,謹以此開篇,作為學習和練習Python的起步。

——————————學習分割線————————————

第一次學習的是Python爬取圖片資訊。

網上有很多很多關於這一類東西的教程,我也僅僅是可以實現,並且停留在一知半解的程度,在程式碼過程中添加了很多對Python的新的理解,對程式設計這個大集合的更深層的理解。

網上的任何東西都可以看成資源,一個網站可能就是一段html+css,一張圖片可能就是某個地址下的XXX.jpg檔案,無數的網路資源存放在網際網路上,人們通過地址(URL,統一資源定位符)來訪問這些資源,大致過程如下:

使用者在瀏覽器中輸入訪問地址,瀏覽器向伺服器傳送HTTP(或者HTTPS)請求(其中請求資源常用get請求,提交資料常用post請求,post也可做資料請求)。伺服器接收到了這些請求之後找到對應的資源返回給瀏覽器,再經過瀏覽器的解析,最終呈現在使用者面前。

這就是使用者上網的一個簡單的過程。那麼,如果我們需要大量的從網上請求資料,依靠人工一個個得機械操作顯然是不現實的,這時候爬蟲就起作用了。

一開始先要搞清楚什麼是爬蟲。

其實本質上來說爬蟲就是一段程式程式碼。任何程式語言都可以做爬蟲,只是繁簡程度不同而已。從定義上來說,爬蟲就是模擬使用者自動瀏覽並且儲存網路資料的程式,當然,大部分的爬蟲都是爬取網頁資訊(文字,圖片,媒體流)。但是人家維護網站的人也不是傻的,大量的使用者訪問請求可以視為對伺服器的攻擊,這時候就要採取一些反爬機制來及時阻止人們的不知道是善意的還是惡意的大量訪問請求(當然了= =不這樣人家伺服器吃棗爆炸)。

網站爬取過程中我們會碰到以下幾種情況:

1.直接載入資源無處理

2.使用ajax非同步載入

3.帶引數驗證的載入

4.cookie驗證

5.登入驗證

6.js加密

第一種無需解釋,第二種是使用者訪問過程中異步向伺服器傳送請求會給篩選爬取資料增加難度,第三種是引數驗證例例如時間戳,ip等,第四種是追蹤驗證使用者的本地資料,第五種是身份驗證,第六種是載入後對資料進行加密操作增加爬取難度。

本次學習只處理到第三層,帶引數驗證的網站載入。

學習時爬取的是這個網站(如有打擾,十分抱歉)http://tu.duowan.com/m/bizhi


先對這個網站進行分析。大部分都是套圖,有一張圖作為封面,並且,點選進入圖片之後,有一個顯示第一張圖片的連結。


那麼我們第一步可以在主介面的html程式碼中抽取出這些套圖起始的連結地址,這裡顯然需要用到正則來提取這些不同地址。

那麼,有了每個套圖的起始地址之後,我們進入到子頁面,重新整理網頁,觀察它的載入流程。

這裡我們可以觀察到,僅僅是一張圖片的載入就進行了如此多的工作,這個網站相對來說還是很複雜的(以後會放上一些相對來說不復雜的網頁對比非常明顯)


這裡我們點選XHR,會發現chrome自動為我們篩選出了一個XHR物件,這是其使用ajax技術的證明,點選之後我們發現裡邊帶的json資料跟我們想要的套圖資訊其實是一致的


隨意點開一個發現裡邊的資訊都是鍵值對的形式存在著,這裡我們發現source一欄,選定該地址新建標籤訪問



這就是這張圖片在網路上的地址(URL,統一資源定位符)

至此分析全部結束。

我們需要理清楚我們到底要做什麼。

1.抓取主頁面HTML程式碼,篩選出所有的套圖連結

2.從套圖頁面中獲取json,從資訊中獲取該套圖的全部URL

3.根據URL下載圖片到本地

4.重複2,3

下面掛上一個學習使用的程式碼

import json
import os
import re
import time
import requests
import pymysql

def strip(path):
    path = re.sub(r'[?\\*|"<>:/]', '', str(path))
    return path

# 爬蟲類
class Spider:
    #初始化函式
    def __init__(self):
        self.session = requests.session()

    # 下載圖片
    def download(self, url):
        try:
            return self.session.get(url)
        except Exception as e:
            print(e)

    # 從start_url開始獲取全部的套圖入口,並逐個獲取套圖資訊,最終儲存圖片
    def run(self, start_url):
        #獲取全部的套圖入口id
        img_ids = self.get_img_ids(start_url)
        print(img_ids)
        #並逐個獲取套圖資訊,最終儲存圖片
        for img_id in img_ids:
            #獲取套圖json資訊
            img_item_info = self.get_img_item_info(img_id)
            print(img_item_info)
            self.save_img(img_item_info)
            #exit()#僅迴圈一次

    # 下載html程式碼,並從中篩選出套圖入口id,返回套圖id列表
    def get_img_ids(self, start_url):
        response = self.download(start_url)
        if response:
            html = response.text
            #正則仍然有待學習
            ids = re.findall(r'http://tu.duowan.com/gallery/(\d+).html', html)
            return ids

    # 為了拿到json資料,我們觀察檔案中存在兩個數字,一個是套圖入口id我們已知,另一個是時間戳,可以仿製
    def get_img_item_info(self, img_id):
        # http://tu.duowan.com/index.php?r=show/getByGallery/&gid=125658&_=1519643852451
        img_item_url = "http://tu.duowan.com/index.php?r=show/getByGallery/&gid={}&_={}".format(img_id,
                                                                                                int(time.time() * 1000))
        #下載後解析得到可用的json資料
        response = self.download(img_item_url)
        if response:
            return json.loads(response.text)

    # 套圖資訊持久化
    def save_img(self,img_item_info):
        #以套圖示題建立資料夾儲存套圖
        dir_name = img_item_info['gallery_title']
        #字串剪下去掉無法做資料夾的字串
        strip(dir_name)
        print(dir_name)
        #建立資料夾
        if not os.path.exists(dir_name):
            os.makedirs(dir_name)
        #獲取圖片名,圖片url,圖片字尾,儲存圖片路徑等
        for img_info in img_item_info['picInfo']:
            img_name = img_info['title']
            strip(img_name)
            img_url = img_info['url']
            pix = (img_url.split('/')[-1]).split('.')[-1]
            img_path = os.path.join(dir_name, "{}.{}".format(img_name, pix))#此處路徑拼接避免出現字串問題
            print(dir_name,img_name,img_path)
            #儲存圖片至本地
            if not os.path.exists(img_path):
                response = self.download(img_url)
                if response:
                    print(img_url)
                    img_data = response.content
                    with open(img_path, 'wb') as f:
                        f.write(img_data)
            #在本地資料庫中插入記錄
            db = pymysql.connect("localhost", "root", "123456", "python", use_unicode=True,
                                 charset="utf8");  # 此處會出現編碼問題
            cursor = db.cursor()
            try:
                sql = "INSERT INTO img (dir_name,img_title,img_path) VALUE (%s,%s,%s);"  # 此處寫法可以避免轉義問題
                cursor.execute(sql, (dir_name, img_name, img_path))
                db.commit()
            except Exception as e:
                print(e)
                db.rollback()
            db.close()


if __name__ == '__main__':

    spider = Spider()
    spider.run('http://tu.duowan.com/m/bizhi')
    #spider.download('http://tu.duowan.com/gallery')

程式碼僅供學習參考使用,也是給自己起一個回顧的作用。

程式碼中仍然存在缺陷,例如資料庫開關操作過多等,以後的優化後期再做處理。

時常提醒自己多多練習Python。