1. 程式人生 > >利用python requests庫模擬登陸知乎

利用python requests庫模擬登陸知乎

當初搜模擬登陸的時候在知乎上也找到一些內容。

以下是程式碼

import requests
import time
import json
import os
import re
import sys
import subprocess
from bs4 import BeautifulSoup as BS


class ZhiHuClient(object):

    """連線知乎的工具類,維護一個Session
    2015.11.11

    用法:

    client = ZhiHuClient()

    # 第一次使用時需要呼叫此方法登入一次,生成cookie檔案
    # 以後可以跳過這一步
    client.login("username", "password")

    # 用這個session進行其他網路操作,詳見requests庫
    session = client.getSession()
    """

    # 網址引數是賬號型別
    TYPE_PHONE_NUM = "phone_num"
    TYPE_EMAIL = "email"
    loginURL = r"http://www.zhihu.com/login/{0}"
    homeURL = r"http://www.zhihu.com"
    captchaURL = r"http://www.zhihu.com/captcha.gif"

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate",
        "Host": "www.zhihu.com",
        "Upgrade-Insecure-Requests": "1",
    }

    captchaFile = os.path.join(sys.path[0], "captcha.gif")
    cookieFile = os.path.join(sys.path[0], "cookie")

    def __init__(self):
        os.chdir(sys.path[0])  # 設定指令碼所在目錄為當前工作目錄

        self.__session = requests.Session()
        self.__session.headers = self.headers  # 用self呼叫類變數是防止將來類改名
        # 若已經有 cookie 則直接登入
        self.__cookie = self.__loadCookie()
        if self.__cookie:
            print("檢測到cookie檔案,直接使用cookie登入")
            self.__session.cookies.update(self.__cookie)
            soup = BS(self.open(r"http://www.zhihu.com/").text, "html.parser")
            print("已登陸賬號: %s" % soup.find("span", class_="name").getText())
        else:
            print("沒有找到cookie檔案,請呼叫login方法登入一次!")

    # 登入
    def login(self, username, password):
        """
        驗證碼錯誤返回:
        {'errcode': 1991829, 'r': 1, 'data': {'captcha': '請提交正確的驗證碼 :('}, 'msg': '請提交正確的驗證碼 :('}
        登入成功返回:
        {'r': 0, 'msg': '登陸成功'}
        """
        self.__username = username
        self.__password = password
        self.__loginURL = self.loginURL.format(self.__getUsernameType())
        # 隨便開個網頁,獲取登陸所需的_xsrf
        html = self.open(self.homeURL).text
        soup = BS(html, "html.parser")
        _xsrf = soup.find("input", {"name": "_xsrf"})["value"]
        # 下載驗證碼圖片
        while True:
            captcha = self.open(self.captchaURL).content
            with open(self.captchaFile, "wb") as output:
                output.write(captcha)
            # 人眼識別
            print("=" * 50)
            print("已開啟驗證碼圖片,請識別!")
            subprocess.call(self.captchaFile, shell=True)
            captcha = input("請輸入驗證碼:")
            os.remove(self.captchaFile)
            # 傳送POST請求
            data = {
                "_xsrf": _xsrf,
                "password": self.__password,
                "remember_me": "true",
                self.__getUsernameType(): self.__username,
                "captcha": captcha
            }
            res = self.__session.post(self.__loginURL, data=data)
            print("=" * 50)
            # print(res.text) # 輸出指令碼資訊,除錯用
            if res.json()["r"] == 0:
                print("登入成功")
                self.__saveCookie()
                break
            else:
                print("登入失敗")
                print("錯誤資訊 --->", res.json()["msg"])

    def __getUsernameType(self):
        """判斷使用者名稱型別
        經測試,網頁的判斷規則是純數字為phone_num,其他為email
        """
        if self.__username.isdigit():
            return self.TYPE_PHONE_NUM
        return self.TYPE_EMAIL

    def __saveCookie(self):
        """cookies 序列化到檔案
        即把dict物件轉化成字串儲存
        """
        with open(self.cookieFile, "w") as output:
            cookies = self.__session.cookies.get_dict()
            json.dump(cookies, output)
            print("=" * 50)
            print("已在同目錄下生成cookie檔案:", self.cookieFile)

    def __loadCookie(self):
        """讀取cookie檔案,返回反序列化後的dict物件,沒有則返回None"""
        if os.path.exists(self.cookieFile):
            print("=" * 50)
            with open(self.cookieFile, "r") as f:
                cookie = json.load(f)
                return cookie
        return None

    def open(self, url, delay=0, timeout=10):
        """開啟網頁,返回Response物件"""
        if delay:
            time.sleep(delay)
        return self.__session.get(url, timeout=timeout)

    def getSession(self):
        return self.__session

if __name__ == '__main__':
    client = ZhiHuClient()

    client.login('xxxxxx','xxxxxxxx')
    # 第一次使用時需要呼叫此方法登入一次,生成cookie檔案
    # 以後可以跳過這一步
    # client.login("username", "password")

    # 用這個session進行其他網路操作,詳見requests庫
    session = client.getSession()
    r=session.get('http://www.zhihu.com')
    print(s.text)
來自知乎:

這模擬登陸的程式碼可以作為參考。

最後是關於獲取天氣預報的爬蟲程式碼:

import urllib.request
import re
def GetHtmlCode(url):
    page = urllib.request.urlopen(url)
    htmlCode = page.read().decode('gbk')
    page.close()
    return htmlCode

def FindGXUrl(homePage):
    gx_re_vague=r'<a href="[\S]+" rel="[\S]+">江蘇</a>'
    gx_url_vague=re.search(gx_re_vague,homePage).group()
    gx_re=r'http://[\w\./]+\.htm'
    gx_url=re.search(gx_re,gx_url_vague).group()
    return gx_url

def FindNNUrl(GXPage):
    by_re_vague=r'<a href="[\S]+?" title="[\S]+?">南京</a>'
    nn_url_vague=re.search(by_re_vague,GXPage).group()
    by_re=r'/[\S]+?\.htm'
    nn_url_suffix=re.search(by_re,nn_url_vague).group()
    return nn_url_suffix

def GetWeatherBlockList(WeatherPage):
    weatherBlock_re=r'<li class="week-detail-now" >[\s\S]+?</li>'
    weather_re=re.compile(weatherBlock_re)
    weatherList=re.findall(weather_re,WeatherPage)
    return weatherList

class Weather:
    date=''
    daytime=''
    nighttime=''
    temperatureL=''
    temperatureH=''
    def __init__(self,d,dT,nT,tL,tH):
        self.date=d
        self.daytime=dT
        self.nighttime=nT
        self.temperatureL=tL
        self.temperatureH=tH
    def print(self):
        print('\n%s:白天:%s,夜間:%s,\n最低溫度:%sC,最高溫度:%sC\n'%(self.date,self.daytime,self.nighttime,self.temperatureL,self.temperatureH))



def MakeWeatherInfo(block):
    dA_re=r'[\d]{2}月[\d]{2}日'
    dA=re.search(dA_re,block).group()

    dT_re=r'<b><font class="gray">白天:</font>.{1,6}</b>'
    dT=re.search(dT_re,block).group()
    dT=re.sub(r'<b>.+</font>','',dT)
    dT=re.sub(r'</b>','',dT)

    nT_re=r'<b><font class="gray">夜間:</font>.{1,6}</b>'
    nT=re.search(nT_re,block).group()
    nT=re.sub(r'<b>.+</font>','',nT)
    nT=re.sub(r'</b>','',nT)

    t_re=r'<font class="blue">.{0,4}</font>~<font class="red">.{0,4}</font>'
    t=re.search(t_re,block).group()
    t=re.findall(r'[\d]+',t)
    return Weather(dA,dT,nT,t[0],t[1])
homePage=GetHtmlCode("http://tianqi.2345.com/")
gx_url=FindGXUrl(homePage)
GXPage=GetHtmlCode(gx_url)
nn_url_suffix=FindNNUrl(GXPage)
nn_url='http://tianqi.2345.com'+nn_url_suffix

NNPage=GetHtmlCode(nn_url)
weatherList=GetWeatherBlockList(NNPage)#get a list of two days' weather

weather1=MakeWeatherInfo(weatherList[0])
weather2=MakeWeatherInfo(weatherList[1])
weather1.print()
weather2.print()
思路很簡單。但主要也是正則表示式的書寫,還是得勤加練習才對。

用Python實現爬蟲的確非常簡單。但是利用scrapy框架之類的剛接觸一會發現利用Python3連安裝都是各種error.累覺不愛。

現在都是單執行緒。以後能做多執行緒和分散式爬蟲的時候再回來補充吧。

之後幾個月打算研究django,但是這估計也是個很大的坑呢233333.還得學習SQL語言balabala..挑戰性很足。

如果學到什麼東西在往部落格裡放吧。記錄一下學習的過程。

大學實在是太枯燥了。也許是我不太喜歡社交呢233333。