python实战案例

✨博主介绍

个人主页:苏州程序大白

个人社区:CSDN全国各地程序猿

作者介绍:中国DBA联盟(ACDU)成员,CSDN全国各地程序猿(媛)聚集地管理员。目前从事工业自动化软件开发工作。擅长C#、Java、机器视觉、底层算法等语言。2019年成立柒月软件工作室,2021年注册苏州凯捷智能科技有限公司

有任何问题欢迎私信,看到会及时回复

微信号:stbsl6,微信公众号:苏州程序大白

如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)

想加入技术交流群的可以加我好友,群里会分享学习资料

爬虫简单入门

爬虫合法性-君子协议
  • 关于爬虫的合法性,有君子协议
    在网站网址后加上/robots.txt查看君子协议
准备注意事项
  • 做爬虫前尽量不要使用任何网络代理,否则容易出现莫名的问题
手刃一个小爬虫(request模块实现)
  • 简单试做:将百度搜索源码爬取:
#百度
#需求:用程序模拟浏览器,输入一个网址,从该网址中获取到资源或者内容
from urllib.request import urlopen      #从包中导入模块

url="http://www.baidu.com"          #准备网址
resp = urlopen(url)            #用urlopen模拟浏览器打开网址,将返回的响应存入resp

"""
先print(resp.read())查看返回的内容
从中找到编码格式,一般为charset后位置
再进行解码

print(resp.read().decode("utf-8"))         #resp.read()从响应中读取内容,并用decode解码
"""

with open("D:\desktop\代码\python测试\Mywebsite.html",mode="w",encoding="utf-8") as web:            #打开名为"Mywebsite.html"的文件,模式为w写入,as语句将其简称为web,设置encoding打开编码
    web.write(resp.read().decode("utf-8"))              #resp.read()从响应中读取内容,并用decode解码,将其写入到上述文件

Web 请求、HTTP 协议、抓包

Web 请求过程解析
  • 1.服务器渲染:在服务器直接把数据和 html 整合在一起,统一返回给浏览器。
    举例:输入**www.baidu.com**,浏览器向百度服务器发送请求,百度返回 html 页面源代码;在百度里搜索关键词,百度在服务器将关键词有关数据写入 html 页面源代码中,一并返回给浏览器
  • 2.客户端渲染:第一次请求只要一个 html 骨架,第二次请求拿到数据,进行数据展示。在页面源代码中,看不到数据。
    举例:例如豆瓣电影排行榜的分类筛选网页,浏览器先向服务器请求,服务器返回 html 骨架(不包含数据),浏览器第二次请求,服务器返回数据,浏览器将 html 骨架与数据渲染结合,呈现页面。在源代码处搜索呈现的数据,无法找到。
  • 熟练使用浏览器抓包工具
    Chrome 浏览器右键检查或者 F12,上方大类选择 Network;
    刷新页面,此时所有返回的请求都在此处显示。点击文件可以打开源代码,通常第一个文件为网页骨架;
    Headers 中 Request URL 写有 url 地址,Preview 可以查看预览效果。在这些文件中通过预览找到和页面内容匹配的数据,回到 Headers 即可找到数据 url
  • 想要得到数据无需骨架,对于爬虫而言,目的为得到数据,骨架无影响
HTTP 协议
  • HTTP 协议基本概念

    • 协议:两台计算机之间为了能流畅的进行沟通而设置的一个君子协定,常见的协议有 TCP/IPSOAP 协议,HTTP 协议,SMTP 协议等

    • HTTP 协议:Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传输协议。直白点儿,浏览器和服务器之间的数据交互遵守的就是 HTTP 协议

    • HTTP 协议把一条消息分为

      三大块内容

      ,无论是请求还是响应都是三块内容

      • 请求

        1、请求行 → 请求方式(get/post),请求 url 地址,协议
        2、请求头 → 放一些服务器要使用的附加信息
        3、请求体 → 一般放一些请求参数

      • 响应

        1、状态行 → 协议,状态码
        2、响应头 → 放一些客户端要使用的附加信息
        3、响应体 → 服务器返回的真正客户端要用的内容(HTML,json 等)

  • 抓包工具及获得的重要信息

    • Network-Headers-General:一般信息

      Request URL:URL 地址
      Request Method:请求方式
      Status Code:状态码

    • Network-Headers-

      Response Headers

      响应头

      cookie:本地字符串数据信息(用户登录信息,反爬的 token)
      其他:各种神奇的莫名其妙的字符串(这个需要经验,一般都是 token 字样,防止各种攻击和反爬)

    • Network-Headers-

      Request Headers

      请求头

      User-Agent:请求载体的身份标识(用啥发送的请求,如浏览器信息)
      Referer:防盗链(这次请求是从哪个页面来的,反爬需要)
      cookie:本地字符串数据信息(用户登录信息,反爬的 token)

    附:请求方式

    • Get:显示提交(常用于搜索,通常只读)
    • Post:隐式提交(常用于对数据增删改,通常可写入)

requests 模块入门

模块安装
  • requests 模块为第三方支持库,需要手动安装
pip install requests
Requests 入门-1

GET 请求:将搜狗搜索内容爬取,并学习简单的反爬

import requests

url = "https://www.sogou.com/web?query=周杰伦"          #保存网址字符串给变量,中文可能转码错误,手动打上去
#第10行处被拦截,可以将更多请求头信息补入,定义一个字典headers,将User-Agent写入字典,User-Agent通过抓包网页骨架中的Request Headers(请求头)找到,注意直接复制后Mozilla前会多一个空格,记得删除
dict = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36"}

#用get请求方式请求url,所有地址栏中的url都是get方式请求,将响应存入resp。第四行信息补充完成后,将字典写入headers参数,处理简单的反爬
resp = requests.get(url,headers=dict)

#print(resp)         #打印resp,返回网页状态码,返回200正常
print(resp.text)        #打印页面源代码,但爬虫被拦截了,前往第四行补充信息

resp.close()            #关闭请求

可以进行一些小修改,做到更改搜索对象:

import requests

#手动输入搜索的内容
query=input("输入你要搜索的内容:")
#利用f-string,做到搜索内容更改
url = f"https://www.sogou.com/web?query={query}"          #保存网址字符串给变量,中文可能转码错误,手动打上去
#第10行处被拦截,可以将更多请求头信息补入,定义一个字典headers,将User-Agent写入字典,User-Agent通过抓包网页骨架中的Request Headers(请求头)找到,注意直接复制后Mozilla前会多一个空格,记得删除
dict = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36"}

#用get请求方式请求url,所有地址栏中的url都是get方式请求,将响应存入resp。第四行信息补充完成后,将字典写入headers参数,处理简单的反爬
resp = requests.get(url,headers=dict)

#print(resp)         #打印resp,返回网页状态码,返回200正常
print(resp.text)        #打印页面源代码,但爬虫被拦截了,前往第四行补充信息

resp.close()            #关闭请求
Requests 入门-2

POST 请求:爬取百度翻译的结果

"""
打开百度翻译后按F12进入抓包工具,清除多余的文件,注意输入法切换为英文,输入英文单词后,翻译框下方有一个小列表
在抓包工具中通过preview预览尝试寻找列表的数据文件,发现sug文件为数据文件
打开sug文件的Headers,获取需要的信息:url地址,请求方式为POST
打开Payload,找对From Data,为POST传参数据,对于上个GET程序中利用f-string传入参数的方式就不灵了
"""
import requests

url = "https://fanyi.baidu.com/sug"         #准备url,注意url为数据的url,即sug文件Headers的url

word = input("请输入你要翻译的英文:")        #准备翻译的单词
dat = {"kw":word}               #由于POST传参数据来源为From Data,所以按照From Data中的格式,将搜索数据改写入字典,此时可以通过变量更改数据

resp = requests.post(url,data=dat)             #由于网页访问方式为POST,故使用POST访问,将dat传入data参数,即传入From Data。将响应存入resp
#print(resp.text)               #输出发现文件有乱码,可以另外直接输出json文件
print(resp.json())                #将服务器返回的内容直接处理成json(),按照python字典方式输出

resp.close()            #关闭请求

#总结,对于POST请求,发送的数据必须放在字典中,通过data参数进行传递
Requests 入门-3

浏览器渲染的二次 GET 请求网页
豆瓣电影分类排行榜-喜剧

  • 通常网站 url 里有问号”?”,问号前的是 url,问号后的是参数
"""
豆瓣电影分类排行榜网页通过浏览器渲染,有两次数据传递
在抓包工具中选择筛选XHR类别(常表示二次请求数据),找到跟页面差不多的蕴含量大一些的XHR文件,就是页面的数据文件找到数据文件Headers:
查看url,通常网站url里有问号"?",问号前的是url,问号后的是参数,查看请求方式为GET方式
在Payload中有Query String Parameters(url问号后参数),
"""
import requests

url = "https://movie.douban.com/j/chart/top_list"        #参数过长,可以重新封装url参数,url问号后参数部分可以删除

#重新封装参数。将抓包Query String Parameters的参数复制进字典,分别打双引号,加逗号
param = {
    "type": "24",
    "interval_id": "100:90",
    "action":"" ,
    "start": "0",
    "limit": "20"
}

header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36"}        #布置user-agent

resp = requests.get(url,params=param,headers=header)              #将param的参数传入params(截止params)。将返回的响应存入resp
#print(resp.request.url)         #输出按照参数重组后的url地址
#print(resp.text)                #code 0,什么都没有,说明被反爬了
print(resp.json())

resp.close()            #关闭请求

#反爬处理

#首先尝试修改user-agent
#print(resp.request.headers)         #(补充)查看默认信息,user-agent
#获取浏览器抓包user-agent,准备(第20行),写入requests.get的参数
#成功拿到数据,但有乱码,将24行优化为25行,获取json文件
  • 在豆瓣中下拉,刷新出新的电影,同时 Query String Parameters 中出现新的数据,与原数据对比发现只有 Query String Parameters 的 start 参数变化,可以借此修改代码中 start 参数实现新效果

数据解析

数据解析概述
  • 爬取到的网站内容和数据被夹在了

    html
    

    内,想要提取需要的数据,这便涉及到了数据提取

    本课程提供三种解析方式:

    Re 解析(理论运行速度最快)
    Bs4 解析(代码简单,但效率较低)
    Xpath 解析(目前较流行,中规中矩)

  • 三种方式可以混合进行使用,完全以结果做导向,只要能拿到想要的数据,用什么方法不重要,当掌握了这些之后再考虑性能的问题

Re 解析_正则表达式
  • Re 解析:Regular Expression 的简写,正则表达式,一种使用表达式的方式对字符串进行匹配的语法规则
  • 我们抓取到的网页源代码本质上就是一个超长的字符串。,想从里面提取内容,用正则表达式再合适不过了
  • 优点:速度快效率高准确性高
    缺点:新手上手难度较大
  • 不过只要掌握了正则编写的的逻辑关系,写出一个提取页面内容的正则并不复杂
  • 正则的语法:使用元字符进行排列组合用来匹配字符串
    在线测试正则表达式https://tool.oschina.net/regex/
  • 元字符:具有固定含义的特殊符号
  • 常用元字符
    python实战案例_第1张图片
  • 量词:控制前面的元字符出现的次数
    python实战案例_第2张图片
  • 贪婪匹配惰性匹配
    img
    这两个着重说一下,写爬虫用的最多的就是惰性匹配
    *?表示尽可能少的让*匹配东西
Bs4 解析_HTML 语法
  • Bs4 解析:Beautiful Soup4 的简写,简单易用的 HTML 解析器,需要掌握一些 HTML 语法
  • HTML(Hyper Text Markup Language)超文本标记语言,是编写网页最基本最核心的语言,其语法就是用不同的标签,对网页上的内容进行标记,从而使网页显示不同的效果,简单举例:
<h1>I Love Youh1>
  • 常用标签
    python实战案例_第3张图片
  • 属性:标签内后跟的控制标签行为属性,其后所写的为属性值,简单举例:
<h1 align="right">I Love Youh1>

借此实现标题文字右对齐,其中,align为属性,right为属性值

  • 由此,HTML基本语法格式为:
<标签 属性="" 属性="">被标记的内容标签>
Xpath 解析_XML 概念
  • Xpath 解析:XML 解析器,用来提取XML 文档中的节点,Xpath 是在 XML 文档中搜索的一门语言。HTML 是 XML 的一个子集
  • 基础概念
<book>
  <id>1id>
  <name>野花遍地香name>
  <price>1.23<price>
  <author>'
    <nick>周大强nick>
    <nick>周芷若nick>
  author>
book>

在上述 html 中:

  1. book,id,name,price等都被称为节点
  2. id,name,price,author被称为book子节点book被称为他们的父节点
  3. id,name,price,author被称为同胞节点

python 实现 Re 解析

Python 的 re 模块使用

在 python 中使用正则表达式,可以使用re模块,re模块记住几个常用功能就足够我们日常使用了:

import re           #引入re模块

#findall:匹配字符串中所有的符合正则的内容
list = re.findall("\d+","我的电话号是10086,我朋友的电话是10010")           #findall的结果是一个列表
print(list,"\n")

#列表效率低下,面对大量数据难以应对,按如下处理
#finditer:匹配字符串中所有的内容[返回的是迭代器],从迭代器中遍历拿到内容需要.group()函数
it = re.finditer("\d+","我的电话号是10086,我朋友的电话是10010")
#print(it)
for i in it:
    print(i.group())
print()

#search返回的结果是match对象,那数据需要.group(),此外search全文检索,检索到一个就直接返回
s = re.search("\d+","我的电话号是10086,我朋友的电话是10010")
#print(s)
print(s.group(),"\n")

#match从头开始匹配,可以认为默认在正则前加了^符号,如下方10086前加一个非数字,则匹配为空
a = re.match("\d+","10086,我朋友的电话是10010")
print(a.group(),"\n")

#compile预加载正则表达式,能够提高一定的运行效率
obj = re.compile("\d+")
#此时obj即预加载\d+的正则,下次使用可以obj.函数,如下:
ret = obj.finditer("我的电话号是10086,我朋友的电话是10010")
#print(ret)
for it in ret:
    print(it.group())
print()

#用正则表示全部文本信息
s="""
雷军
李彦宏
张小龙
马云
马化腾
"""
obj1 = re.compile("
.*?
"
,re.S) #re.S作用:让点.能匹配换行符 #
(?P.*?)
"
,re.S) #re.S作用:让点.能匹配换行符 #