python爬蟲彙總

1.數據獲取方式

xpath

/#未完成

import requests
from lxml import html as lxml_html  #pip install lxml 用於解析html
html = requests.get(url = 'http://www.baidu.com')
html.encoding = 'utf-8'
html = html.text
doc = lxml_html.fromstring(html)
title = doc.xpath('這裏是xpath規則')

>>>title
['匹配的內容']

正則


最常用的 (.*?) 非貪婪匹配 (.*?) 默認匹配所有
如果遇到換行問題 ([\s\S]*) 貪婪匹配

例:

import re
string = '{"vm_type":"kvm","ve_status":"running","ve_mac1":"***********","ve_used_disk_space_b":5088407552,"ve_disk_quota_gb":"10","is_cpu_throttled":"","ssh_port":29364,"live_hostname":"ubuntu","load_average":"0.00 0.00 0.00 1\\/167 27303","mem_available_kb":345856,"swap_total_kb":135164,"swap_available_kb":100160,"hostname":"localhost.localdomain","node_ip":"**********","node_alias":"v7415","node_location":"US, California","node_location_id":"USCA_3","node_datacenter":"US: Los Angeles, California (DC3 CN2)","location_ipv6_ready":false,"plan":"kvmv3-10g-512m-500m-ca-cn2","plan_monthly_data":536870912000,"monthly_data_multiplier":1,"plan_disk":10737418240,"plan_ram":536870912,"plan_swap":0,"plan_max_ipv6s":0,"os":"ubuntu-16.04-x86_64","email":"[email protected]","data_counter":14346176667,"data_next_reset":1532495173,"ip_addresses":["**********"],"rdns_api_available":true,"ptr":{"********":null},"suspended":false,"error":0,"veid":938308}'
re_plan = '"plan":"(.*?)"'
plan = re.findall(re_plan,string)

>>>plan
['kvmv3-10g-512m-500m-ca-cn2']


re_plan = '"plan":"([\s\S]*)"'
plan = re.findall(re_plan,string)

>>>plan
['kvmv3-10g-512m-500m-ca-cn2","plan_monthly_data":536870912000,"monthly_data_multiplier":1,"plan_disk":10737418240,"plan_ram":536870912,"plan_swap":0,"plan_max_ipv6s":0,"os":"ubuntu-16.04-x86_64","email":"[email protected]","data_counter":14346176667,"data_next_reset":1532495173,"ip_addresses":["**********"],"rdns_api_available":true,"ptr":{"********":null},"suspended":false,"error":0,"veid']

css

暫時還沒有接觸過。。。

2.string 轉 json

import json
from pprint import pprint
string = '{"vm_type":"kvm","ve_status":"running","ve_mac1":"***********","ve_used_disk_space_b":5088407552,"ve_disk_quota_gb":"10","is_cpu_throttled":"","ssh_port":29364,"live_hostname":"ubuntu","load_average":"0.00 0.00 0.00 1\\/167 27303","mem_available_kb":345856,"swap_total_kb":135164,"swap_available_kb":100160,"hostname":"localhost.localdomain","node_ip":"**********","node_alias":"v7415","node_location":"US, California","node_location_id":"USCA_3","node_datacenter":"US: Los Angeles, California (DC3 CN2)","location_ipv6_ready":false,"plan":"kvmv3-10g-512m-500m-ca-cn2","plan_monthly_data":536870912000,"monthly_data_multiplier":1,"plan_disk":10737418240,"plan_ram":536870912,"plan_swap":0,"plan_max_ipv6s":0,"os":"ubuntu-16.04-x86_64","email":"[email protected]","data_counter":14346176667,"data_next_reset":1532495173,"ip_addresses":["**********"],"rdns_api_available":true,"ptr":{"********":null},"suspended":false,"error":0,"veid":938308}'
j_son = json.loads(string)
pprint(j_son) #能夠帶格式輸出

3.關於一些常見編碼問題通用方案

import requests
from pprint import pprint
url = "http://www.baidu.com"
html = requests.get(url)
html.encoding = 'utf-8'
pprint(html.text)

大致效果
這裏寫圖片描述

4.logging

爲什麼要用logging :
  • 我記得有個 皮皮怪這麼回答 一個計算式 返回結果 計算 耗時1% print 耗時 99%
當然,寫python隨心就行,但是大項目還是得用logging來做下記錄的,小項目 print + file wirte 也沒的事
import logging
import requests

class Project(object):
    def __init__(self):
        #如果你覺得有些無關緊要的logging太礙眼  比方說 requests 的url請求的logging記錄等,可以通過下行代碼(通過設置logging的等級 )
        #logging.getLogger("requests").setLevel(logging.WARNING)
        self.logger = logging.getLogger()
        handler = logging.StreamHandler()
        formatter = logging.Formatter('%(asctime)s [%(threadName)s][%(levelname)s] %(message)s') 
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)#這裏設置logging的等級


具體到某一步的logging記錄 !!!注意不管是debug還是 info還是warning 括號內把必須是字符串

self.logger.debug("這裏來記錄一些內容") #不侷限於debug 還有 info 、warning 括號內 type = string

5.關於一些比較常用的字符串處理

字符串去除 空格 換行符

string.strip()

字符串分割

#比如以':'爲界分割
string.split(':')

字符串替換

string = "w:w:m"
a=string.replace(':','/')

>>>a
'w/w/m'

6.python切片

爲什麼要講切片
當我們的爬蟲獲取到信息時候,不可能是十全十美的,總是會有殘缺的情況的
比如:
我們應該拿到的完整的信息是這個樣子的

info = {
'title':'titel_content',
'content':['1','2','3'],
'tags':['life','love'],
'author':['name:horsun','introduce:~~~~~~~~']# 比如 author是的list 長度爲2 
}
但是實際上我們拿到了這個樣子的:
info = {
'title':'titel_content',
'content':['1','2','3'],
'tags':['life','love'],
'author':['name:horsun']#但是 有些內容缺失 導致只有一個長度
}


所以當我們要獲取introduce的時候應該是info['author'][1]
但是當我們的數據出現第二種情況的時候 info['author'][1] 就會拋錯 list長度錯誤
但是我們可以通過切片獲取 info['author'][1]的對象
info['author'][0:1] #獲取的是info['author'][1] 但是 這樣返回的是一個list ------>['name:horsun']
info['author'][0:1] 第一個0 是起始位置 第二個1 是終止位置 類似數學的區間 [4,8)---->4,5,6,7
所以 我們可以通過判斷 info['author'][1:2] 的長度 ---->info['author'][1:2].__len__() ==0


info = {
'title':'titel_content',
'content':['1','2','3'],
'tags':['life','love'],
'author':['name:horsun']#但是 有些內容缺失 導致只有一個長度
}
article = Article()
article.create(
title = info['title'],
content= info['content'],
tags= info['tags'],
author_name= info['author'][0]   if info['author'][0:1].__len__() !=0 else ' ',
author_intro= info['author'][1]   if info['author'][1:2].__len__() !=0 else ' ',
)


if info['author'][1:2].__len__() !=0 else ''
關於這句 就是 如果info['author'][1:2].__len__() !=0 成立
就執行 author_name= info['author'][0]
否則就執行 author_name=' '

7.一些常見的反 反爬 手段

  • 設置headers 即 請求頭
    -基本上的url設置一個UA(User-Agent)就行了,除非一些特例要完全按照抓包的請求 headers來
    例:headers2 = {
    'Host': 'jwxt.zwu.edu.cn',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit /537.36(KHTML, like Gecko)Chrome / 67.0.3396.62 Safari / 537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Referer': 'http://jwxt.zwu.edu.cn/xs_main.aspx?xh=2014014701',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh,en;q=0.9,zh-CN;q=0.8',
  • time sleep #注意不要固定一個sleep時間 可以 random 否則也會判定你是機器人 (哪有人能控制時間這麼準確的)
    例 :
import time
import random

time.sleep(random.randint(5, 10))
  • 設置代理 (效率最高)# requests爲例
import requests
url = 'http://www.baidu.com'
proxies = {
    "http":"110.110.110.110:110"   #"協議"+:+"ip+端口號"
}
html = requests.get(url,proxies = proxies)
  • 通過 selenium 來爬 (萬能,但是效率較低)
    有代理誰用 selenium
  • 以及一些攜帶奇葩的post參數
  • 記錄登陸 session
    下面講一個 asp.NET的奇葩post參數例子
    大學校園教務網個人成績頁查詢頁爬蟲。
    頁面是asp.net編寫的。

    ·——————————————————·
    先通過登陸頁面 url1 登陸來記錄session,然後請求成績查詢頁面(還沒有數據的頁面) url2 ,請求查詢全部成績 url3。
    其實講道理,一般的網站,你記錄了登陸session,所有頁面都是隨心所欲的,所已url2也能正常訪問,但是當我打算獲取我的所有成績的時候,通過請求url3發現,請求失敗,並沒有出現我想要的成績數據,
    通過抓包發現,其實url2和url3 是同一個url,但是url2 是get方法,url3 是post方法,
    發現 url3 在post的時候發送了一個 '__VIEWSTATE' 和一個 '__VIEWSTATEGENERATOR'參數,通過網上查找發現 這倆個參數其實是通過url2的get請求 插在了html中 在url3發送post請求的時候 攜帶了了這倆個參數,服務器接受請求的時候驗證了這倆個參數纔給相應

注意,這倆個url請求的headers也不完全相同,一定要按照抓包的時候請求頭 headers來請求

這裏寫圖片描述
代碼片段

    def get_response_data(self):
        """
        通過第一次訪問 self.url 來獲取 第二次訪問 self.url的的所需參數
        第一次 訪問self.url 是get方法
        第二次 訪問self.url 是post方法 post帶了寫 data 所需參數 需要從第一次訪問所返回的html中找到
        (第一次是get方法 請求返回後 html 內有__VIEWSTATE 和__VIEWSTATEGENERATOR 倆大參數)
        :return:
        """
        response = self.session.get(url=self.url4.format(self.student_id),
                                    headers=self.headers2,
                                    )
        response.encoding = 'gb2312'
        html = response.text
        __VIEWSTATE = re.findall('name="__VIEWSTATE" value="(.*?)"', html)
        __VIEWSTATEGENERATOR = re.findall('name="__VIEWSTATEGENERATOR" value="(.*?)"', html)
        self.data = {
            '__VIEWSTATE': ''.join(__VIEWSTATE),
            '__VIEWSTATEGENERATOR': ''.join(__VIEWSTATEGENERATOR),
            'ddlXN': '',
            'ddlXQ': '',
            'Button1': '%B0%B4%D1%A7%C6%DA%B2%E9%D1%AF', }

    def get_score(self):
        """
        獲取目標url的html內容
        接下來就可以對目標頁面進行解析
        可以通過正則或者xpath來提取數據
        :return:
        """
        cookies = self.session.cookies
        response = self.session.post(url=self.url4.format(self.student_id),
                                     data=self.data,
                                     headers=self.score_headers,
                                     cookies=cookies
                                     )
        response.encoding = 'gb2312'
        html = response.text
        print(html)#這一步就獲取到了所有的頁面數據信息了 ✌
        doc = lxml_html.fromstring(html)

完整代碼 :https://github.com/helloworld19951213/get_my_school_scroe/blob/master/spider.py

8.增量爬取/去重(重複爬取)/更新爬取

  • 去重和增量爬取 主要思想就是把已經爬過的數據放到列表裏面,通過判斷url或者別的參數 是否再列表中 來決定是否爬取

    詳細見我另一篇文章 https://blog.csdn.net/qq_33042187/article/details/78929834
    ——————————————————分割線————————————————————————————

待補充 2018/7/4


TODO

-> 應對 常見的反爬 有效手段 7.5
->數據去重
->增量爬取
->多線程爬蟲 以及 多線程+隊列實現線程間的通信
->一些抓包手段
待補充

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