python库(1)

python的json库

使用过json5,数据量大时速度不能忍。

orjson 速度比较快。

安装(conda-forge):

conda install orjson

requests库

使用curl调试调用参数

建议copy了 curl后, 从 https://curl.trillworks.com/ 获取代码修改

SSLError: HTTPSConnectionPool

参考: https://www.cnblogs.com/hum0ro/p/9536033.html

方案1:添加参数 verify=False

response = requests.get('http://www.baidu.com/', headers = header, verify=False)  

会有提示 InsecureRequestWarning: Unverified HTTPS request is being made to host, 消除该警告:

import urllib3
urllib3.disable_warnings()

# 在请求代码 requests.get(url...) 前添加如下代码即可
requests.packages.urllib3.disable_warnings()
r = requests.get(url...)

方案2:安装如下的模块,不一定有用

conda install cryptography pyOpenSSL certifi

10060错误: ProtocolError: ('Connection aborted.', TimeoutError(10060...

可以尝试的解决方案:

  • 延长访问频率: time.sleep(15)
  • 随机切换User-Agent:
 user_agent_list = ["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
                    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
                    "Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/61.0",
                    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
                    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
                    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
                    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
                    "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
                    ]
                headers['User-Agent'] = random.choice(user_agent_list)

UnicodeEncodeError

报错信息:

UnicodeEncodeError: 'latin-1' codec can't encode character '\u2026' in position 30: ordinal not in range(256)

有可能是 User-Agent 等参数里有特殊字符, 比如下面的 (…):

"Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/61.0",

ConnectionError

报错信息类似:

ConnectionError: HTTPSConnectionPool(host='www.xxx.org', port=443): Max retries exceeded with url

http的连接数超过最大限制,默认的情况下连接是Keep-alive的,所以这就导致了服务器保持了太多连接而不能再新建连接. 解决方式:

  • header中不使用持久连接: 'Connection': 'close',
  • 请求前设置: requests.adapters.DEFAULT_RETRIES = 5

类似连接这类问题,使用JMS这类代理一般都可以解决,只是连接速度有可能降低.

##使用Numba加速python科学计算代码

win10下安装Numba

安装参考Numba官网

windows10平台下的安装(NVIDIA显卡),其中cuda是NVIDIA显卡工具:

conda install numba
conda install cudatoolkit 

检查安装:

C:\>python
Python 3.8.5 (default, Sep  3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numba
>>> numba.__version__
'0.51.2'


 C:\>numba
numba: error: the following arguments are required: filename

(base) C:\>numba --sysinfo
System info:
--------------------------------------------------------------------------------
__CUDA Information__
CUDA Device Initialized                       : True
CUDA Driver Version                           : 11000
CUDA Detect Output:
Found 1 CUDA devices
id 0     b'GeForce GTX 1650'                              [SUPPORTED]
                      compute capability: 7.5
                           pci device id: 0
                              pci bus id: 1
Summary:
        1/1 devices are supported

速度测试

参考: numba,让python速度提升百倍

# -*- coding: utf-8 -*-

from numba import njit,float64
import datetime

# 不使用numba的情况
def f_1(n):
    x = 123.45
    y = 678.9
    for i in range(1,n):
        temp = x * y / i

    return temp

# 使用 numba
@njit
def f_2(n):
    x = 123.45
    y = 678.9
    for i in range(1,n):
        temp = x * y / i

    return temp


# 使用 numba
@njit (float64(float64))
def f_3(n):
    x = 123.45
    y = 678.9
    for i in range(1,n):
        temp = x * y / i

    return temp


start=datetime.datetime.now()
r1 = f_1(100000000)
elapsed_1 = (datetime.datetime.now() - start)  # 秒数
elapsed_1 = elapsed_1.microseconds

start=datetime.datetime.now()
r2=f_2(100000000)
elapsed_2 = (datetime.datetime.now() - start)  # 秒数
elapsed_2 = elapsed_2.microseconds

start=datetime.datetime.now()
r3=f_3(100000000)
elapsed_3 = (datetime.datetime.now() - start)  # 秒数
elapsed_3 = elapsed_3.microseconds

print("函数1消耗微秒数:",elapsed_1)
print("函数2消耗微秒数:",elapsed_2)
print("函数1消耗微秒数:",elapsed_3)
print("函数1消耗微秒数/函数2消耗微秒数:",elapsed_1/elapsed_2)

print('r1,r2,r3:',r1,r2,r3)
'''输出:
函数1消耗微秒数: 250370
函数2消耗微秒数: 34907
函数1消耗微秒数: 0
函数1消耗微秒数/函数2消耗微秒数: 7.17248689374624
r1,r2,r3: 0.0008381020583810206 0.0008381020583810206 0.0008381020583810206
'''

数值计算中, Numba提升明显,指定参数类型时提速更夸张。一定用njit注解强制转换,并指定数据类型

python中含有None等情形或其他逻辑处理的写法:

def func_without_numba():
    # 调用含有 Numba 代码的函数
    try:
        func_with_numba()
    except:
        # 这里写异常处理代码
        pass

@njit(参数)
def func_with_numba()
    # 不需要处理异常的数值计算代码
    pass

使用pexpect和paramiko

paramiko示例

使用密匙登录到服务器,执行命令并回显

import paramiko # conda install  paramiko

private_key = paramiko.RSAKey.from_private_key_file('path_to_keyfile')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname = server_ip, port=server_port,\
            username=server_username, pkey=private_key)
stdin, stdout, stderr = ssh.exec_command('ifconfig')

result = stdout.read()
print(result.decode())
ssh.close()

pexpect示例

使用密匙通过 ssh同步本地和远程文件夹。

windows系统下,脚本中不能使用 pexpect 包,所以该脚本不支持windows。

import pexpect  # conda install pexpect
# private_key = $HOME/.ssh/somekey
# 必须在 $HOME/.ssh/ 目录下
# chmod 700 /home/userName/.ssh/somekey
# private_key = '/home/userName/.ssh/somekey'

cmd:rsync  --delete -avzP  /本地文件夹路径/ -e 'ssh -p port_num_str -i private_key ' root@远程服务器IP::rsync模块名

ssh = pexpect.spawn(cmd,timeout = timeout,encoding='utf-8')
ssh.logfile_read = sys.stdout # 返回执行过程中的内容
i = ssh.expect('Password:')
if i==0:
    ssh.sendline(rsync密码)
    ssh.read()
    ssh.close()

DBUtils:一套Python数据库连接池包

DBUtils是一套Python数据库连接池包,允许对非线程安全的数据库接口进行线程安全包装。DBUtils 仅提供给了连接池管理,实际的数据库操作依然是由符合 DB-API 2 标准的目标数据库模块完成的。

DBUtils提供两种外部接口:

  • PersistentDB :提供线程专用的数据库连接,并自动管理连接-。
  • PooledDB :提供线程间可共享的数据库连接,并自动管理连接。

实测证明 PersistentDB 的速度是最高的,但是在某些特殊情况下,数据库的连接过程可能异常缓慢,而此时的PooledDB则可以提供相对来说平均连接时间比较短的管理方式。可以考虑直接用PooledDB.

连接池对象只初始化一次,一般可以作为模块级代码来确保。

示例参考: https://cito.github.io/DBUtils/UsersGuide.html

PostgreSQL连接池示例,需要先安装相关的模块:

conda install psycopg2
pip install DBUtils

示例代码:

import psycopg2   
from dbutils.pooled_db import PooledDB  


pgpool = PooledDB(creator=psycopg2,mincached=0,maxcached=10,\
                  host='127.0.0.1',
                  port=5432,
                  user='postgres',
                  password='pgpassword',
                  database='pgdb')

sql = 'select * from tbname;'
db = pgpool.connection()
cur = db.cursor()
cur.execute(sql)
result =  cur.fetchall()           
cur.close()
db.close()

BeautifulSoup库

安装:conda install beautifulsoup4

获取指定class样式的div

soup=BeautifulSoup(html)
result = soup.find_all(name='div',attrs={"class":"footer"})#按照字典的形式给attrs参数赋值

GuessedAtParserWarning

添加类似 lxml的解析器:

soup = BeautifulSoup(r.text,"lxml")

示例: 抓取弯曲日报第一期内容并解析

URL:https://wanqu.co/issues/1

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup

def get_wanqu_article(issue):
    # 获取湾区日报 wanqu.co 的第 issue 期html 页面内容
    print("抓取第" + str(issue) + "期文章")
    url = "https://wanqu.co/issues/" + str(issue)
    r = requests.get(url)
    r.encoding="UTF-8"
    if r.status_code==200:
        html = r.text
    else:
        print("抓取湾区日报第" + str(issue) + "期失败")
        html = ''

    '''
    解析获取的 html,输出文章条目:
    - 弯曲日报期数:2014/08/06 第1期
        - 文章题目:StackOverflow: 25台服务器,每月560,000,000 page views
        - 文章源链: 原链 highscalability.com 
        - 点评:湾区日报作者点评
    '''

    if len(html) > 0:
        soup = BeautifulSoup(html,"lxml")

        # 获取title
        tags = soup.find_all('h1', class_="wq-header")
        title = tags[0].text

        # 获取所有文章在wanqu.co的链接
        '''
        <a href="https://wanqu.co/a/9/stackoverflow-25台服务器每月560000000-page-views/"
            style="color: #000;" title="StackOverflow: 25台服务器,每月560,000,000 page views">
            <h2 class="wq-header" style="margin-bottom:4px;">StackOverflow: 25台服务器,每月560,000,000
                page views</h2>
        </a>
        '''
        tags = soup.find_all('a')
        article_url_list = []
        for tag in tags: 
            tags2 = tag.find_all('h2', class_="wq-header")
            if len(tags2)>0:
                article_url_list.append(tag.get('href'))

        # 抓取并解析每一篇文章
        article_list = []
        for url in article_url_list:
            r = requests.get(url)
            r.encoding="UTF-8"
            if r.status_code==200:
                article_html =  r.text
            else:
                print("抓取文章html失败:" + url)
                article_html = ''
            if len(article_html)>0:
                # [title,url,lead]
                article_info = parse_article_html(article_html)
                article_list.append(article_info)
        return [title,article_list]
    else:
        return []

def parse_article_html(article_html):
    soup = BeautifulSoup(article_html,"lxml")
    # 获取title
    tags = soup.find_all('h1', class_="wq-header")
    title = tags[0].text

    # 获取原链
    tags = soup.find_all('a')
    for i in range(len(tags)):
        if "原链" in tags[i].text:
            url = tags[i].get('href')
            url = url[:url.find("?utm_source")]
            break

    # 获取点评
    tags = soup.find_all('div', class_="lead")
    lead = tags[0].text.strip()

    return [title,url,lead]


if __name__=="__main__":
    info = get_wanqu_article(1)

示例: 抓取必应(bing)每日壁纸脚本

本脚本从 https://bing.ioliu.cn/ 获取图片,并保存到指定目录

# -*- coding: utf-8 -*-
# 获取 bing(必应)的每日壁纸
# bing官方 api只能获取7天的,参考:https://blog.csdn.net/m0_37682004/article/details/82314055
# 本脚本从 https://bing.ioliu.cn/ 获取图片

import requests
#  conda install beautifulsoup4 lxml
from bs4 import BeautifulSoup 
import os
from PIL import Image # conda install Pillow

def get_all_file_names(local_dir):
    # 获取指定目录下的所有文件名,返回文件名列表
    pic_file_list=[]
    for root, dirs, files in os.walk(local_dir):
        for name in files:
            pic_file_list.append(name)
    return pic_file_list

def get_html_content(pageNum):
    # 页码url: https://bing.ioliu.cn/?p=1, 首页为1
    url = "https://bing.ioliu.cn/?p=" + str(pageNum)
    print("获取第 " + str(pageNum) + "页html内容")
    html = requests.get(url)
    return html.content

def get_pic_url_list(htmlContent):
    # 获取html中的图片 url列表
    '''
<img class="progressive__img progressive--is-loaded" 
src="http://h1.ioliu.cn/bing/BubbleNebula_ZH-CN2787112807_1920x1080.jpg" 
data-progressive="http://h1.ioliu.cn/bing/BubbleNebula_ZH-CN2787112807_1920x1080.jpg">
    '''
    soup = BeautifulSoup(htmlContent, 'lxml')
    pics = soup.findAll('img')
    url_list = []
    for pic in pics:
        if 'src' in pic.attrs:
            url_list.append(pic.attrs['src'])
    return url_list

def save_bing_pics(pageNumStart,pageNumEnd,local_dir):
    pic_file_list = get_all_file_names(local_dir)
    for pageNum in range(pageNumStart,pageNumEnd +1):
        htmlContent = get_html_content(pageNum)
        url_list = get_pic_url_list(htmlContent)
        for pic_url in url_list:
            pic_name = pic_url.split('/')[-1]
            if pic_name not in pic_file_list:
                pic_file_list.append(pic_name)
                print("获取图片:" + pic_name)
                html = requests.get(pic_url)
                with open(os.path.join(local_dir,pic_name),'wb') as file:
                    print("保存图片:" + pic_name)
                    file.write(html.content)
            else:
                print("图片已存在:" + pic_name)

def clear_pic_files(width,height,local_dir):
    # 清理图片文件,删除分辨率小于  width * height 的图片
    fileNameListToDel = []
    all_file_names = get_all_file_names(local_dir)
    for file_name in all_file_names:
        with Image.open(os.path.join(local_dir,file_name)) as imgFile:
            # 宽,高
            #print("图片 " + file_name + ":" + str(imgFile.width) + "*" + str(imgFile.height) )
            if (imgFile.width < width) or (imgFile.height < height):
                fileNameListToDel.append(file_name)
    # 删除文件
    for file_name in fileNameListToDel:
        os.remove(os.path.join(local_dir,file_name))
if __name__=="__main__":
    local_dir="D:/壁纸/bing"
    save_bing_pics(1,4,local_dir)  
    clear_pic_files(1920,1080,local_dir)  

正文完
 
评论(没有评论)