python爬虫个人学习笔记

在这里插入图片描述

1.URI 是统一资源标识符(Universal Resource Identifier),URL 是统一资源定位符(Universal Resource Locator),URI 是用字符串来标识某一互联网资源,而 URL 则是表示资源的地址(我们说某个网站的网址就是 URL),因此 URI 属于父类,而 URL 属于 URI 的子类。
url网页地址:由三部分组成
第一部分是协议:http https ftp file ed2k…
第一部分与第二部分用:// 隔开
第二部分是存放自愿的服务器域名系统或ip地址(有时也要包含端口号,各种传输协议都有默认的端口号,如http的默认端口号是80)
第二部分与第三部分用 / 隔开
第三部分是资源的具体地址,如目录或文件名等。
在这里插入图片描述
设计爬虫时应该特别注意什么问题:不要重复爬取同一个 URL 的内容。假设你没做这方面的预防,如果一个 URL 的内容中包含该 URL 本身,那么就会陷入无限递归。
如果你是网站的开发者,你如何禁止百度爬虫访问你网站中的敏感内容?:在网站的根目录下创建并编辑 robots.txt 文件,用于表明您不希望搜索引擎抓取工具访问您网站上的哪些内容。此文件使用的是 Robots 排除标准,该标准是一项协议,所有正规搜索引擎的蜘蛛均会遵循该协议爬取。既然是协议,那就是需要大家自觉尊重,所以该协议一般对非法爬虫无效。
urllib.request.urlopen() 返回的是什么类型的数据?

import urllib.request
response=urllib.request.urlopen("http://www.baidu.com")
print(type(response))#<class 'http.client.HTTPResponse'>

返回的是一个HTTPResponse的实例对象,它属于http.client模块
如何查看网页采用什么编码传输的? chrome浏览器,右键网页,检查
在这里插入图片描述
为了解决 ASCII 编码的不足,产生了Unicode编码

如果访问的网址不存在,会产生哪类异常 HTTPError

chardet(一个模块),使用它就可以检测字符串的编码

import urllib.request
import chardet
response=urllib.request.urlopen("http://www.fishc.com").read()
print(chardet.detect(response))#{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
#confidence指置信度
#来判断使用的编码
#GBK是GB2312的扩展,GBK向下兼容GB2312

检测指定 URL 的编码

import urllib.request
import chardet
def main():
    url=input("请输入URL:")

    response=urllib.request.urlopen(url)
    html=response.read()


    #识别网页编码
    encode=chardet.detect(html)['encoding']
    if encode=='GB2312':
        encode='GBK'

    print("该网页使用的编码是:%s"% encode)

if __name__=='__main__':
    main()

依次访问文件中指定的站点,并将每个站点返回的内容依次存放到不同的文件中

import urllib.request
import chardet
def main():
    i=0
    
    with open("urls.txt",'r') as f:
        #读取待访问的网址
        #由于urls.txt每一行一个url
        #所以按换行符'\n'分割
        urls=f.read().splitlines()
    for each_url in urls:
        response=urllib.request.urlopen(each_url)
        html=response.read()
        
        #识别网页编码
        encode=chardet.detect(html)['encoding']
        if encode=='GB2312':
            encode='GBK'
        i+=1
        filename="url_%d.txt"% i
        
        with open(filename,"w",enoding=encode) as each_file:
            each_file.write(html.decode(encode,"ignore"))

if __name__=='__main__':
    main()

2.urllib是一个包由四部分组成:urllib.request,urllib.error,urllib.parse,urllib.robotparser,第一块最为复杂,包含了对服务器的请求,发出,跳转,代理,安全等
3.request的部分应用:

import urllib.request
#response=urllib.request.urlopen("http://placekitten.com/g/500/600")#括号里面既可以是字符串也可以是request对象
req=urllib.request.Request("http://placekitten.com/g/500/600")#初始化一个request对象,可以初始化data,headers等参数
response=urllib.request.urlopen(req)
cat_img=response.read()
#print (html)
#html=html.decode("utf-8")
#print(html)
with open('cat_500_600.jpg','wb') as f:
    f.write(cat_img)#下载网站的图

客户端和服务器之间进行请求和响应的时候,由两种最常用的办法,一种是GET(从服务器请求获得数据),一种就是POST(向指定服务器提交被处理的数据)
在这里插入图片描述
data参数若未被赋值,以默认none形式,则为GET,若赋值了,则以POST形式提交,且data必须遵循以下格式:
在这里插入图片描述
(如何通过 urlopen() 使用 POST 方法向服务端发出请求?urlopen 函数有一个 data 参数,如果给这个参数赋值,那么 HTTP 的请求就是使用 POST 方式;如果 data 的值是 NULL,也就是默认值,那么 HTTP 的请求就是使用 GET 方式。)
可以利用urllib.parse.urlencode()来转化成此格式;
json:轻量级的数据交换格式,以字符串形式把Python给封装起来,便于存储和使用
利用有道进行翻译:

import urllib.request
import urllib.parse
import json
def main():
    ur1="http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"#去掉translate中的_o可正常显示
    #利用chrome浏览器右键检查,选择network,查看Headers中的Request URL得到
    content=input("请输入需要翻译的内容:")
    data={}
    data['type']='AUTO'
    data['i']=content
    data['doctype']= 'json'
    data['version']= '2.1'
    data['keyfrom']= 'fanyi.web'
    data['ue']='UTF-8'
    data['typoResult']='true'
    data=urllib.parse.urlencode(data).encode('utf-8')
    #同上的Form Data得到,把里面的东西以字典形式写出来
    #发送用户请求
    response=urllib.request.urlopen(ur1,data)
    #读取并解码内容
    html=response.read().decode('utf-8')
    #decode的作用是将其他编码的字符串转换成 unicode 编码,相反,encode 的作用是将 unicode 编码转换成其他编码的字符串
    print(html)
    target=json.loads(html)
    #print(target,"-------",type(target))
    print("翻译结果:%s" %(target['translateResult'][0][0]['tgt']))
if __name__=='__main__':
    main()

4.有些网站不希望被爬虫程序所访问,因此会检查链接的来源,如果来源不是正常的途径,就会阻止你,为了代码能持续的使用,我们需要对代码进行一些隐藏,不像程序访问而是像普通人正常点击,(通过检查链接的Headers的User-Agent,来判断是来自于代码的访问,还是来自于浏览器,比如python访问,显示为python加上版本号)所以我们通过修改headers来模拟正常访问
下图为,正常访问网页时,user-agent数据
在urllib.request.Request()方法中有参数headers,通过修改此参数可设置自己的headers(headers必须是字典),有两种方法,第一种,直接设置一个字典作为参数返回给方法
在这里插入图片描述
在这里插入图片描述
第二种,在request生成后通过add_header()把其加入进去
在这里插入图片描述

User-Agent 属性通常是记录什么信息 :普通浏览器会通过该内容向访问网站提供你所使用的浏览器类型、操作系统、浏览器内核等信息的标识

import urllib.request
import urllib.parse
import json
def main():
    url="http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"#去掉translate中的_o可正常显示
    #利用chrome浏览器右键检查,选择network,查看Headers中的Request URL得到
    content=input("请输入需要翻译的内容:")
    '''方法一手动设置head
    head={}
    head['user-AGENT']='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    '''

    data={}
    data['type']='AUTO'
    data['i']=content
    data['doctype']= 'json'
    data['version']= '2.1'
    data['keyfrom']= 'fanyi.web'
    data['ue']='UTF-8'
    data['typoResult']='true'
    data=urllib.parse.urlencode(data).encode('utf-8')
    #同上的Form Data得到,把里面的东西以字典形式写出来
    req=urllib.request.Request(url,data)
    # 法二
    req.add_header('user-AGENT','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36')
    response=urllib.request.urlopen(req)
    html=response.read().decode('utf-8')
    print(html)
    target=json.loads(html)
    #print(target,"-------",type(target))
    print("翻译结果:%s" %(target['translateResult'][0][0]['tgt']))
    print(req.headers)
    #{'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}

if __name__=='__main__':
    main()

5.若当你上述方法爬取数据,虽然看不出是程序所为,但是你的操作可能在短时间超过一个阈值,对方可能还是会把你屏蔽(记录ip的访问频率,单位时间超过一个阈值,认为有很大可能是爬虫,直接忽略user-AGENT直接返回一个验证码页面,用户会填写,而爬虫无法识别就会把其拒绝了),如何解决:1.延迟提交时间,2.使用代理
加入延时代码:
(效率不高)

import urllib.request
import urllib.parse
import json
import time
def main():
    while True:
        url="http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"#去掉translate中的_o可正常显示
        #利用chrome浏览器右键检查,选择network,查看Headers中的Request URL得到
        content=input('请输入需要翻译的内容(输入”!q“退出程序):')
        if content=='!q':
            break
        '''方法一手动设置head
        head={}
        head['user-AGENT']='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
        '''

        data={}
        data['type']='AUTO'
        data['i']=content
        data['doctype']= 'json'
        data['version']= '2.1'
        data['keyfrom']= 'fanyi.web'
        data['ue']='UTF-8'
        data['typoResult']='true'
        data=urllib.parse.urlencode(data).encode('utf-8')#利用方法对data进行编码改成url的形式
        #同上的Form Data得到,把里面的东西以字典形式写出来
        req=urllib.request.Request(url,data)
        # 法二
        req.add_header('user-AGENT','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36')
        response=urllib.request.urlopen(req)
        html=response.read().decode('utf-8')
        #print(html) {"type":"EN2ZH_CN","errorCode":0,"elapsedTime":1,"translateResult":[[{"src":"love","tgt":"爱"}]]}
        target=json.loads(html)
        #print(target,"-------",type(target))
        print("翻译结果:%s" %(target['translateResult'][0][0]['tgt']))
        #print(req.headers)
        #{'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}
        time.sleep(5)
    print("结束程序!")
if __name__=='__main__':
    main()

代理:用多个代理发起访问,把得到消息反馈给你
在这里插入图片描述
opener是可以自己设置的,指定相关的headers,代理ip。3a会替换掉默认的opener,使用3b则是利用你设定的opener来打开网页;

import urllib.request
import random
url='http://whatismyapp.com'
#设计一个iplist,多加几个ip进去,每次随机使用一个ip
iplist=['119.6.144.73:81','111.1.32.28:81','183.203.208.168:8118']
#proxy_support=urllib.request.ProxyHandler({'http':'119.6.144.73:81'})
proxy_support=urllib.request.ProxyHandler({'http':random.choice(iplist)})
opener=urllib.request.build_opener(proxy_support)
#修改header值
opener.addheaders=[('user-AGENT','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36')]
# 安装opener urllib.request.install_opener((opener))
response=opener.open(url)
html=response.read().decode('utf-8')

6.urllopen()补充
urllib.request.urlopen()函数用于实现对目标url的访问
函数原型:urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None) 
url: 需要打开的网址
data:Post提交的数据
timeout:设置网站的访问超时时间
–直接用urllib.request模块的urlopen()获取页面,page的数据格式为bytes类型,需要decode()解码,转换成str类型。
函数参数详解
(1). url 参数:目标资源在网路中的位置。可以是一个表示URL的字符串(如:http://www.pythontab.com/);也可以是一个urllib.request对象
(2). data参数:data用来指明发往服务器请求中的额外的参数信息(如:在线翻译,在线答题等提交的内容),data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
(3). timeout:设置网站的访问超时时间
(4). cafile、capath、cadefault 参数:用于实现可信任的CA证书的HTTP请求。(基本上很少用)
(5). context参数:实现SSL加密传输。(基本上很少用)
返回处理方法
urlopen返回对象提供方法:
read() , readline() ,readlines() , fileno() , close() :对HTTPResponse类型数据进行操作
info():返回HTTPMessage对象,表示远程服务器返回的头信息
getcode():返回Http状态码。如果是http请求,200请求成功完成;404网址未找到
geturl():返回请求的url

import urllib.request
def main():
    url="http://placekitten.com/g/500/600"
    response=urllib.request.urlopen(url)
    print(response.geturl())#http://placekitten.com/g/500/600
    print(response.info())
    '''Date: Thu, 10 Sep 2020 07:35:47 GMT
Content-Type: image/jpeg
Transfer-Encoding: chunked
Connection: close
Set-Cookie: __cfduid=d78b391398020a5e5cbd5b5626b0370f61599723347; expires=Sat, 10-Oct-20 07:35:47 GMT; path=/; domain=.placekitten.com; HttpOnly; SameSite=Lax
Cache-Control: public, max-age=86400
Expires: Thu, 31 Dec 2020 20:00:00 GMT
Vary: User-Agent, Accept-Encoding
Access-Control-Allow-Origin: *
CF-Cache-Status: HIT
Age: 1366
cf-request-id: 05188a36a70000dccee8a4d200000001
Server: cloudflare
CF-RAY: 5d07796aaed7dcce-SIN'''
    print(response.getcode())#表示http的状态,200表示正常响应,404就是出错

if __name__=='__main__':
    main()

版本区别,注意事项
python2和python3在导入urlrequest的方式都不一样。

python2是这样:import urllib2

而python3里面把urllib分开了,分成了urlrequest和urlerror,在这里我们只需导入urlrequest即可。from urllib.request import urlopen

原来的 urllib.urlencode() 变为 urllib.parse.urlencode().encode(),由于编码的关系,你还需要在后边加上 encode(‘utf-8’);

cookielib 被改名为 http.cookiejar

HTTP 是基于请求-响应的模式:发出请求的永远是客户端,做出响应的永远是服务端

7.配合 EasyGui,给“下载一只猫“的代码增加互动:

  1. 让用户输入尺寸;
  2. 如果用户不输入尺寸,那么按默认宽400,高600下载喵;
  3. 让用户指定保存位置。
    在这里插入图片描述

在这里插入图片描述
代码如下:

import easygui as g
import urllib.request

def main():
    msg="请填写~o( =∩ω∩= )m的尺寸"
    title="下载一只喵"
    fieldNames=["宽:","高:"]
    fieldValues=[]
    size = width, height = 400, 600
    fieldValues=g.multenterbox(msg,title,fieldNames,size)
    while 1:
        if fieldValues==None:
            break
        errmsg=""

        try:
            width=int(fieldValues[0].strip())
        except:
            errmsg+="宽必须是整数!"

        try:
            height = int(fieldValues[1].strip())
        except:
            errmsg += "高度必须是整数!"

        if errmsg=="":
            break
        fieldValues=g.multenterbox(errmsg,title,fieldNames,fieldValues)

    url="http://placekitten.com/g/%d/%d"%(width,height)

    response=urllib.request.urlopen(url)
    cat_img=response.read()

    filepath=g.diropenbox("请选择存放喵的文件夹")

    if filepath:
        filename='%s/cat_%d_%d.jpg'%(filepath,width,height)
    else:
        filename='cat_%d_%d.jpg'%(width,height)
    with open(filename,"wb") as f:
        f.write(cat_img)
if __name__=="__main__":
    main()

cookie:HTTP 协议是基于请求响应模式,就是客户端发一个请求,服务端回复一个响应。但 HTTP 协议是无状态的,也就是说客户端这会儿给服务端提交了账号密码,服务端回复验证通过,但下一秒客户端说我要访问 x 资源,服务端回复:“啊??你是谁?!”为了解决这个尴尬的困境,有人就发明出了 cookie。cookie 相当于服务端(网站)用于验证你的身份的密文。于是客户端每次提交请求的时候,服务端通过验证 cookie 即可知道你的身份信息。那么正如你所猜测的,CookieJar 是 Python 用于存放 cookie 的对象。;
8. 服务器是如何识访问来自浏览器还是非浏览器的:通过发送的 HTTP 头中的 User-Agent 来进行识别浏览器与非浏览器,服务器还以 User-Agent 来区分各个浏览器
代码跟视频中的例子一样,一运行却出错了,但在不修改代码的情况下再次尝试运行却又变好了,这是为什么?:在网络信息的传输中会出现偶然的“丢包”现象,有可能是你发送的请求服务器没收到,也有可能是服务器响应的信息不能完整送回来……尤其在网络阻塞的时候。所以,在设计一个“称职”的爬虫时,需要考虑到这偶尔的“丢包”现象
Request 是由客户端发出还是由服务端发出?:我们之前说 HTTP 是基于“请求-响应”模式,Request 即请求的意思,而 Response 则是响应的意思。由客户端首先发出 Request,服务器收到后返回 Response
代理服务器是如何工作的?他有时为何不工作了?:将信息传给代理服务器,代理服务器替你向你要访问的服务器发送请求,然后在将服务器返回的内容返回给你。因为有“丢包”现象发生,所以多了一个中间人就意味着会多一层发生“丢包”的几率,且大多数代理并不只为一个人服务,尤其是免费代理。
HTTP 有好几种方法(GET,POST,PUT,HEAD,DELETE,OPTIONS,CONNECT),如何判断使用哪种方法访问服务器?:使用 get_method() 方法获取 Request 对象具体使用哪种方法访问服务器。最常用的无非就是 GET 和 POST 了,当 Request 的 data 参数被赋值的时候,get_method() 返回 ‘POST’,否则一般情况下返回 ‘GET’。
服务器是通过什么来确定你是登陆还是没登陆的么?他会持续到什么时候呢?:是 cookie,服务器通过判断你提交的 cookie 来确定访问是否来自”熟人“
cookie 可以分成两类:一类是即时过期的 cookies,称为“会话” cookies,当浏览器关闭时(这里是 python 的请求程序)自动清除;另一类是有期限的 cookies,由浏览器进行存储,并在下一次请求该网站时自动附带(如果没过期或清理的话)
9. Beautiful Soup 4 文档
编写一个爬虫,爬百度百科“网络爬虫”的词条(链接 -> http://baike.baidu.com/view/284853.htm),将所有包含“view”的链接按下边格式打印出来:


from bs4 import BeautifulSoup
import urllib.request
import re


def main():
    url = " http://baike.baidu.com/view/284853.htm"
    response = urllib.request.urlopen(url)
    html = response.read()
    soup = BeautifulSoup(html, 'html.parser')  # 使用python默认的解析器
    # print(soup.prettify())
   # 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性.如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:
    for link in soup.find_all(href=re.compile("view")):
        print(link.text, "->", ''.join(["http://baike.baidu.com", link['href']]))
        '''恐龙百科 -> http://baike.baidu.com/wikicategory/view?categoryName=恐龙大全
多肉百科 -> http://baike.baidu.com/wikicategory/view?categoryName=多肉植物
锁定 -> http://baike.baidu.com/view/10812319.htm'''

if __name__ == '__main__':
    main()

要求你的爬虫允许用户输入搜索的关键词,然后爬虫进入每一个词条,然后检测该词条是否具有副标题(比如搜索“猪八戒”,副标题就是“(中国神话小说《西游记》的角色)”),如果有,请将副标题一并打印出来在这里插入图片描述

#无法运行
#报错:UnicodeEncodeError: 'ascii' codec can't encode characters in position 36-39: ordinal not in range(128)
from bs4 import BeautifulSoup
import urllib.request
import re

def main():
    keyword=input("请输入关键词:")
    keyword=urllib.parse.urlencode({"word":keyword})
    response=urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
    #访问搜索界面;不像有道那样,增加data参数;
    html=response.read()
    soup=BeautifulSoup(html,"html.parser")

    for each in soup.find_all(href=re.compile("view")):
        content=''.join([each.text])
        url2=''.join(["http://baike.baidu.com", each["href"]])#先得到相关词条的链接,再进入链接去获得信息
        response2 = urllib.request.urlopen(url2)
        html2 = response2.read()
        soup2 = BeautifulSoup(html2, "html.parser")
        if soup2.h2:#副标题
            content = ''.join([content, soup2.h2.text])
        content = ''.join([content, " -> ", url2])#先连接副标题,在链接网址
        print(content)

if __name__ == "__main__":
    main()

哗啦啦地丢一堆链接给用户可不是什么好的体验,我们应该先打印 10 个链接,然后问下用户“您还往下看吗
在这里插入图片描述

import urllib.request
import urllib.parse
import re 
from bs4 import BeautifulSoup

def test_url(soup):
    result = soup.find(text=re.compile("百度百科尚未收录词条"))
    if result:
        print(result[0:-1]) # 百度这个碧池在最后加了个“符号,给它去掉
        return False
    else:
        return True

def summary(soup):
    word = soup.h1.text
    # 如果存在副标题,一起打印
    if soup.h2:
        word += soup.h2.text
    # 打印标题
    print(word)
    # 打印简介
    if soup.find(class_="lemma-summary"):
        print(soup.find(class_="lemma-summary").text)   

def get_urls(soup):
    for each in soup.find_all(href=re.compile("view")):
        content = ''.join([each.text])
        url2 = ''.join(["http://baike.baidu.com", each["href"]])
        response2 = urllib.request.urlopen(url2)
        html2 = response2.read()
        soup2 = BeautifulSoup(html2, "html.parser")
        if soup2.h2:
            content = ''.join([content, soup2.h2.text])
        content = ''.join([content, " -> ", url2])
        yield content
        

def main():
    word = input("请输入关键词:")
    keyword = urllib.parse.urlencode({"word":word})
    response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
    html = response.read()
    soup = BeautifulSoup(html, "html.parser")

    if test_url(soup):
        summary(soup)
        
        print("下边打印相关链接:")
        each = get_urls(soup)
        while True:
            try:
                for i in range(10):
                    print(next(each))
            except StopIteration:
                break
            
            command = input("输入任意字符将继续打印,q退出程序:")
            if command == 'q':
                break
            else:
                continue
    
if __name__ == "__main__":
    main()

10.Base64:用记事本打开exe、jpg、pdf这些文件时,我们都会看到一大堆乱码,因为二进制文件包含很多无法显示和打印的字符,所以,如果要让记事本这样的文本处理软件能处理二进制数据,就需要一个二进制到字符串的转换方法。Base64是一种最常见的二进制编码方法。
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法;
什么情况下需要使用到Base64?Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。什么是可打印字符?在ASCII码中规定,0 ~ 31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。
Base64,就是使用64个可打印字符来表示二进制数据的方法.
在这里插入图片描述
也就是说,如果将索引转换为对应的二进制数据的话需要至多6个Bit(64由六位二进制表示111111-000000)然而ASCII码需要8个Bit来表示,那么怎么使用6个Bit来表示8个Bit的数据呢?6个Bit当然不能存储8个Bit的数据,但是46个Bit可以存储38个Bit的数据啊!如下表所示:
在这里插入图片描述
可以看到“Son”通过Base64编码转换成了“U29u”。这是刚刚好的情况,3个ASCII字符刚好转换成对应的4个Base64字符。但是,当需要转换的字符数不是3的倍数的情况下该怎么办呢?Base64规定,当需要转换的字符不是3的倍数时,一律采用补0的方式凑足3的倍数,具体如下表所示:
在这里插入图片描述
每6个Bit为一组,第一组转换后为字符“U”,第二组末尾补4个0转换后为字符“w”。剩下的使用“=”替代。即字符“S”通过Base64编码后为“Uw==”。这就是Base64的编码过程。(如果要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?Base64用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。)

Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。

import urllib.request
import os

from bs4 import BeautifulSoup
import base64
x=base64.b64encode(b'binary\x00string')
print(x)#b'YmluYXJ5AHN0cmluZw=='

y=base64.b64decode(b'YmluYXJ5AHN0cmluZw==')
print(y)
print("-----------")

#由于标准的Base64编码后可能出现字符+和/,在URL中就不能直接作为参数,所以又有一种"url safe"的base64编码,其实就是把字符+和/分别变成-和_:
z=base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
print(z)

z1=base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
print(z1)

z2=base64.urlsafe_b64decode('abcd--__')
print(z2)
#b'abcd++//'
#b'abcd--__'
#b'i\xb7\x1d\xfb\xef\xff'

'''还可以自己定义64个字符的排列顺序,这样就可以自定义Base64编码,不过,通常情况下完全没有必要。

Base64是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。

Base64适用于小段内容的编码,比如数字证书签名、Cookie的内容等。

由于=字符也可能出现在Base64编码中,但=用在URL、Cookie里面会造成歧义,所以,很多Base64编码后会把=去掉:'''

# 标准Base64:
#'abcd' -> 'YWJjZA=='
# 自动去掉=:
#'abcd' -> 'YWJjZA'
#去掉=后怎么解码呢?因为Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数,因此,需要加上=把Base64字符串的长度变为4的倍数,就可以正常解码了。
def safe_base64_decode(s):
    missing_padding = len(s) % 4
    if missing_padding != 0:
        s += b'=' * (4 - missing_padding)
    return base64.decodestring(s)
#能处理去掉=的base64解码函数
#assert b'abcd' == safe_base64_decode(b'YWJjZA=='), safe_base64_decode('YWJjZA==')
#assert b'abcd' == safe_base64_decode(b'YWJjZA'), safe_base64_decode('YWJjZA')

11.正则表达式:
运用 import re来导入;
在这里插入图片描述

import re
print(re.search(r'fishc','i love fishc.com'))
#<_sre.SRE_Match object; span=(7, 12), match='fishc'>
#第一个参数正则表达式模式用r表示转义字符,原子字符串,不用考虑转义问题,(7,12)理解为[7,12),找不到直接返回None

find方法无法实现的:(通配符:通配符是一种特殊语句,主要有星号和问号(?),用来模糊搜索文件。当查找文件夹时,可以使用它来代替一个或多个真正字符;当不知道真正字符或者懒得输入完整名字时,常常使用通配符代替一个或多个真正的字符。 实际上用“*Not?pad”可以对应Notepad\MyNotepad【*可以代表任何字符串;?仅代表单个字符串,但此单字必须存在】;Notep[ao]d可以对应Notepad\Notepod【ao代表a与o里二选一】,其余以此类推,可表示任何字符, *.docx表示任意名字的文档)
正则化表达式也有通配符.,可以匹配除了换行符之外的任何字符。

z=re.search(r'.','i love fish')
print(z)#<_sre.SRE_Match object; span=(0, 1), match='i'>

z1=re.search(r'fis.','i love fish')
print(z1)#<_sre.SRE_Match object; span=(7, 11), match='fish'>

#当我只想匹配点时,加入反斜杠,这时.不再是通配符,而是单纯的符号
#反斜杠在正则表达式中仍具有剥夺元字符的特殊能力
print(re.search(r'\.','i love fishc.com'))
#<_sre.SRE_Match object; span=(12, 13), match='.'>

#反斜杠也可以使普通字符有强大的能力
#匹配任何数字 \d
print(re.search(r'\d','i love 1233fishc.com'))
#<_sre.SRE_Match object; span=(7, 8), match='1'>

print(re.search(r'\d\d\d','i love 1233fishc.com'))
#<_sre.SRE_Match object; span=(7, 10), match='123'>


#匹配ip地址
print(re.search(r'\d\d\d\.\d\d\d\.\d\d\d\.\d\d\d','192.168.111.123'))
#<_sre.SRE_Match object; span=(0, 15), match='192.168.111.123'>
#缺陷:ip地址最大255,而这样搜索范围在999,且有ip地址部分不是三位数的情况,且样式不好看
print(re.search(r'\d\d\d\.\d\d\d\.\d\d\d\.\d\d\d','192.168.111.1'))#None
#为了表示字符串的范围,可以创建字符类,只要匹配了字符类里任意一个字符,都算匹配
print(re.search(r'[aeiou]','I love fishc.com'))#对大小写敏感,各是各的,若想匹配大写,扩充字符类即可[aeiouAEIOU]
#<_sre.SRE_Match object; span=(3, 4), match='o'>

#在中括号中使用-表示一个范围
print(re.search(r'[a-z]','I love fishc.com'))
#<_sre.SRE_Match object; span=(2, 3), match='l'>

print(re.search(r'[1-9]','I love 123 fishc.com'))
#<_sre.SRE_Match object; span=(7, 8), match='1'>

#{}表示前面的字符重复次数,里面可设置次数范围
print(re.search(r'ab{3}c','abbbc'))
#<_sre.SRE_Match object; span=(0, 5), match='abbbc'>
print(re.search(r'ab{3,10}c','abbbbbbc'))
#在范围内的次数都可以
#<_sre.SRE_Match object; span=(0, 8), match='abbbbbbc'>

print(re.search(r'ab{3}c','abbbbbbc'))#None

#正则表达式匹配字符串,数字对于字符只有0-9
print(re.search(r'[0-255]','188'))#达不到我们想要查找188的结果,只是查了第一个数
#[0-255]实际意味着匹配0-2还有55,即匹配0 1 2 5之间任意一个数字
print(re.search(r'[0-2][0-5][0-5]','188'))#None

# 正确的做法
print(re.search(r'[01]\d\d|2[0-4]\d|25[0-5]','188'))
#<_sre.SRE_Match object; span=(0, 3), match='188'>

#()表示分组,一个组是一个整体
print(re.search(r'(([01]\d\d|2[0-4]\d|25[0-5])\.){3}([01]\d\d|2[0-4][0-4]|25[0-5])','192.155.252.111'))
#<_sre.SRE_Match object; span=(0, 15), match='192.155.252.111'>
#但是这样写只能匹配三位数的,对于一位以及两位则是无法查到

print(re.search(r'(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}([01]{0,1}\d{0,1}\d|2[0-4][0-4]|25[0-5])','192.168.1.1'))
#<_sre.SRE_Match object; span=(0, 11), match='192.168.1.1'>


元字符
在这里插入图片描述

Python3 正则表达式特殊符号及用法(详细列表)
正则表达式的强大之处在于特殊符号的应用,特殊符号定义了字符集合、子组匹配、模式重复次数。正是这些特殊符号使得一个正则表达式可以匹配字符串集合而不只是一个字符串。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#findall,找到搜友匹配的字符串并且把它打包成列表
print(re.findall(r'[a-z]','fish.com'))
#['f', 'i', 's', 'h', 'c', 'o', 'm']
#\在[]里表示转义符
#^在[]里面表示取反,除了这些都要,但是只能放在最前面(放在后面表示匹配脱字符本身)
print(re.findall(r'[^a-z]','Fish.com'))#['F', '.']
print(re.findall(r'[a-z^]','Fish.com'))#['i', 's', 'h', 'c', 'o', 'm']

print(re.search(r'(fishc){1,5}','fishcfishcfishc'))#<_sre.SRE_Match object; span=(0, 15), match='fishcfishcfishc'>
print(re.search(r'(fishc){1, 5}','fishcfishcfishc'))#None 不要随便使用空格,可能会被解析为正则化表达式

贪婪模式:正则匹配默认是贪婪匹配,也就是在满足条件下匹配尽可能多的字符
在这里插入图片描述
非贪婪模式:
在这里插入图片描述
编译正则表达式:
在这里插入图片描述

import re
p=re.compile(r'[a-z]')
print(type(p))#<class '_sre.SRE_Pattern'> 模式对象
#可运用search方法传进待匹配字符串
print(p.search("i love you"))#<_sre.SRE_Match object; span=(0, 1), match='i'>
print(p.findall("i love you"))#['i', 'l', 'o', 'v', 'e', 'y', 'o', 'u']
#模块级别的方法,需要吧正则表达式当作第一个参数传进去,而模式对象是由正则化编译过来的省略了第一个参数,只传入待匹配的字符串

模块的一些方法:
在这里插入图片描述

12.处理异常:
http error是 urlerror子类,写时,要写在其前面,不然无法响应
在这里插入图片描述
在这里插入图片描述
13.scrapy学习:
Scrapy入门教程
在这里插入图片描述
item容器是保存爬取到的数据的容器,其使用方法和python字典类似,并且提供了额外保护机制来避免拼写错误导致的未定义字段错误
在这里插入图片描述
分为爬取两个过程 现在是爬过程,构建一个dmoz_spider.py,并利用cmd执行
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

成功运行完会在当前根目录下生成两个你所爬取的网页的文件;
取过程:
在这里插入图片描述
从我们爬取的内容中,得到title,link,desribe的信息,分别保存并提取出来(从得到的内容提取出我们需要的数据)
在这里插入图片描述
在这里插入图片描述
然后我们需要进入根目录启动scrapy shell+网页url
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
双斜杠加标签名,表示选出这个网页里面所有此标签的元素,返回一个selector对象的列表
在这里插入图片描述
把列表字符串化

若想得到里面的文字,不加标签:加入.text()(比写正则表达式方便)
在这里插入图片描述
通过右键检查获得相关内容的信息,利用xpath进行访问,然后利用text,extract等获取描述信息(注可利用右键之后对所要得到信息再右键可 copy xpath,但是只是当前一项的信息)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

网站的标题:
在这里插入图片描述

网页的链接:
在这里插入图片描述
网站的简介:
在这里插入图片描述

 		item['title']=response.xpath("//div[@class='site-title']/text()").extract()
        item['link']=response.xpath("//div[@class='site-title']/text()").extract()
        item['desc']=response.xpath("//div[@class='site-descr']/text()").extract()

迷惑情况???
在这里插入图片描述
可能是网速原因?
14.关于Python中正则表达式re.S的作用:(如例子所示)

import re
a = """sdfkhellolsdlfsdfiooefo:
877898989worldafdsf"""
b = re.findall('hello(.*?)world',a)
c = re.findall('hello(.*?)world',a,re.S)
print ('b is ' , b)
print ('c is ' , c)
 
 
# 输出结果:
# b is  []
# c is  ['lsdlfsdfiooefo:\n877898989']

只有三单引或者三双引号的情况下,可以直接回车(\n)换行写。其他双引号,单引号写法不同。这里不做其他解释。

在字符串a中,包含换行符\n,在这种情况下:

如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始。

而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
15.下载一个网页的所有内容(js,css,图片,网页)的源码:

import re
import requests
import os
import urllib.request


def get_html(url):
    headers = {'content-type': 'application/json',
               'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'}
    req = urllib.request.Request(url, headers=headers)
    response = urllib.request.urlopen(req)
    html = response.read().decode('gbk', errors='ignore')
    return html


def get_pic(url):
    headers = {'content-type': 'application/json',
               'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'}
    pic_response = requests.get(url, timeout=10, headers=headers)
    pic = pic_response.content
    return pic


def save_file(chdir_path, filename, content):
    if filename[-4:] in ['.jpg', '.png', 'webp', '.png', 'jpeg', '.gif', '.bmp']:
        with open(chdir_path + '/' + 'images/' + filename, "wb+") as f:
            f.write(content)
            return print('写入{}'.format(filename) + '成功')
    elif filename[-2:] == 'js':
        with open(chdir_path + '/' + 'js/' + filename, 'w+') as f:
            f.write(content)
            return print('写入{}'.format(filename) + '成功')
    elif filename[-3:] == 'css':
        with open(chdir_path + '/' + 'css/' + filename, 'w+') as f:
            f.write(content)
            return print('写入{}'.format(filename) + '成功')
    else:
        with open(chdir_path + '/' + filename, 'w+') as f:
            f.write(content)
            return print('写入{}'.format(filename) + '成功')


def scarpy_web(url, web_name):
    local_path = os.getcwd()
    if not os.path.exists(web_name):  # 如果此目录名不存在,那么创建此目录
        os.makedirs(web_name + '/images')
        os.makedirs(web_name + '/css')
        os.makedirs(web_name + '/js')

    # if not os.path.exists(web_name+'/images'):
    #     os.makedirs('images')
    # save html file
    content = get_html(url)
    filename = web_name + '.html'
    chdir_path = local_path + '/' + web_name

    save_file(chdir_path, filename, content)#先储存网页

    # save css file
    patterncss1 = '<link href="(.*?)"'# *?启用非贪婪模式
    patterncss2 = '<link rel="stylesheet" href="(.*?)"'#正则表达式匹配“”里的所有的字符,因为利用()限定了是这一组里的,findall返回括号里的字符串
    patterncss3 = '<link type="text/css" rel="stylesheet" href="(.*?)"'
    result = re.compile(patterncss1, re.S).findall(content)
    result += re.compile(patterncss2, re.S).findall(content)
    result += re.compile(patterncss3, re.S).findall(content)
    # 获取带有css链接的结果

    for link in result:
        css_name_model = '.*/(.*?).css'
        css_filename = re.compile(css_name_model, re.S).findall(link)
        try:
            # 这里匹配出来有些css和js文件是以http链接形式应用的,所以分开来写入
            if link[0] == 'h':
                resopnse1 = get_html(link)
                css_name1 = css_filename[0] + '.css'
                save_file(chdir_path, css_name1, resopnse1)
            else:
                css_url = url + link
                resopnse2 = get_html(css_url)
                css_name2 = css_filename[0] + '.css'#css_filename是列表,会返回列表第一个元素
                save_file(chdir_path, css_name2, resopnse2)
        except Exception as e:
            print('css{}匹配失败'.format(css_filename), '原因:', e)

    # save js file
    patternjs1 = '<script src="(.*?)"'
    patternjs2 = '<script type="text/javascript" src="(.*?)"'
    list = re.compile(patternjs1, re.S).findall(content)
    list += re.compile(patternjs2, re.S).findall(content)

    for i in list:
        js_name_model = '.*/(.*?).js'
        js_filename = re.compile(js_name_model, re.S).findall(i)
        try:
            if i[0] == 'h':
                html1 = get_html(i)
                js_filename1 = js_filename[0] + '.js'
                save_file(chdir_path, js_filename1, html1)
            else:
                js_url = url + i
                html2 = get_html(js_url)
                js_filename2 = js_filename[0] + '.js'
                save_file(chdir_path, js_filename2, html2)
        except Exception as e:
            print('js{}匹配失败'.format(js_filename), '原因:', e)

    # save pic file
    patternimg = '<img src="(.*?)"'
    patternimg2 = '<img.*?src="(.*?)"'
    pic_list = re.compile(patternimg, re.S).findall(content)
    pic_list += re.compile(patternimg2, re.S).findall(content)

    print(pic_list)
    for i in pic_list:
        pic_name_model = '.*/(.*?).(jpg|webp|png|jpeg|gif|bmp)'
        pic_filename = re.compile(pic_name_model, re.S).findall(i)
        try:
            if i[0] == 'h':
                pic1 = get_pic(i)
                pic_filename1 = pic_filename[0][0] + '.' + pic_filename[0][1]
                save_file(chdir_path, pic_filename1, pic1)
            else:
                pic2 = url + i
                pic2 = get_pic(pic2)
                pic_filename2 = pic_filename[0][0] + '.' + pic_filename[0][1]
                save_file(chdir_path, pic_filename2, pic2)
        except Exception as e:
            print('图片{}匹配识别'.format(pic_filename), '原因', e)

if __name__ == '__main__':
    # url = input('输入你想要爬取的网页:')
    url = #输入网页
    web_name = 'whatineed'
    scarpy_web(url=url, web_name=web_name)


15.使用requests发送请求:
在这里插入图片描述

import requests
url='http://www.baidu.com'
rs=requests.get(url)
print(rs.encoding)
print(rs.headers)#里面如果没有content-type,则encoding='utf-8',否则如果设置了charset,就以charset为准,否则就是ISO-8859-1
print(rs.url)
'''ISO-8859-1
{'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html', 'Server': 'bfe/1.0.8.18', 'Pragma': 'no-cache', 'Content-Encoding': 'gzip', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Date': 'Thu, 17 Sep 2020 02:18:04 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:36 GMT'}
http://www.baidu.com/'''
import requests
url='http://www.dianping.com'
header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36'}
rs=requests.get(url,headers=header)#添加header,伪装程序访问;
print(rs.encoding)
print(rs.headers)#里面如果没有content-type,则encoding='utf-8',否则如果设置charset,就以charset为准
#否则就是ISO-8859-1
print(rs.url)
print(rs.status_code)
#print(rs.text)#获取网页内容,会出现乱码,因为编码方式是ISO-8859-1
rs.encoding='utf-8'#使得网页内容正常获取;
print(rs.text)

16.beautifulsoup4补充
在这里插入图片描述

url='http://wsjkw.sc.gov.cn/scwsjkw/gzbd/fyzt.shtml'
res=requests.get(url)
#res.encoding #'ISO-8859-1'直接爬取会有乱码
res.encoding='utf-8'
html=res.text
soup=BeautifulSoup(html)
#soup.find('h2')#<h2>疫情通报</h2>
#soup.find('h2').text#'疫情通报'
a=soup.find('a')#返回第一个找到的a标签
'''<a href="/scwsjkw/gzbd01/2020/9/20/8e9a596e2971493f8a6887b4773f63c7.shtml" target="_blank"><img alt="四川省新型冠状病毒肺炎疫情最新情况(..." src="/scwsjkw/gzbd01/2020/9/20/8e9a596e2971493f8a6887b4773f63c7/images/e76c6db39b6847cca25686855c87b07b.jpg
"/></a>'''
print(a)
print(a.attrs)#返回a的属性,以字典形式
'''{'href': '/scwsjkw/gzbd01/2020/9/20/8e9a596e2971493f8a6887b4773f63c7.shtml', 'target': '_blank'}'''
print(a.attrs['href'])#获取href
#我们再对得到的href拼接上公共部分就可以得到我们想访问的
urlnew='http://wsjkw.sc.gov.cn/'+a.attrs['href']
urlnew='http://wsjkw.sc.gov.cn/'+a.attrs['href']
rs=requests.get(urlnew)
rs.encoding='utf-8'
html=rs.text
soup=BeautifulSoup(html)
#print(soup)
soup.find_all("p")[1]//这样写表示找第几个p元素的内容

17.re补充
在这里插入图片描述
group(0)匹配的是完整的字符串;groups()拿到所有小括号里的内容

import re
pattern="确诊病例(\d+)例"
res2=re.search(pattern,content)
print(content)
print(res2)
'''    9月20日0-24时,四川无新增新型冠状病毒肺炎确诊病例,无新增治愈出院病例,无新增疑似病例,无新增死亡病例。

    截至9月21日0时,全省累计报告新型冠状病毒肺炎确诊病例672例(其中境外输入131例),累计治愈出院652例,死亡3例,目前在院隔离治疗17例,642人尚在接受医学观察。

    9月20日0-24时,全省无新增无症状感染者,当日转为确诊病例0例,当日解除集中隔离医学观察0例。尚在集中隔离医学观察26例(均为境外输入)。

    全省183个县(市、区)全部为低风险区。
<_sre.SRE_Match object; span=(90, 98), match='确诊病例672例'>'''
print(res2.groups())
print(res2.group(0))
print(res2.group(1))
'''('672',)
确诊病例672例
672'''
pattern='确诊病例(\d+)例.*?治愈出院(\d+)例.*?死亡(\d+)例'#.*?非贪心匹配
res2=re.search(pattern,content)
print(res2)
print(res2.groups())
print(res2.group(0))
print(res2.group(1))
print(res2.group(2))
print(res2.group(3))
'''<_sre.SRE_Match object; span=(90, 126), match='确诊病例672例(其中境外输入131例),累计治愈出院652例,死亡3例'>
('672', '652', '3')
确诊病例672例(其中境外输入131例),累计治愈出院652例,死亡3例
672
652
3'''

18.notebook基本操作补充:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值