python实战案例
Posted 苏州程序大白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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/IP
,SOAP
协议,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/ - 元字符:具有固定含义的特殊符号
- 常用元字符
- 量词:控制前面的元字符出现的次数
- 贪婪匹配和惰性匹配
这两个着重说一下,写爬虫用的最多的就是惰性匹配
*?
表示尽可能少的让*
匹配东西
Bs4 解析_HTML 语法
- Bs4 解析:Beautiful Soup4 的简写,简单易用的 HTML 解析器,需要掌握一些 HTML 语法
- HTML(Hyper Text Markup Language)超文本标记语言,是编写网页最基本、最核心的语言,其语法就是用不同的标签,对网页上的内容进行标记,从而使网页显示不同的效果,简单举例:
<h1>I Love You</h1>
- 常用标签:
- 属性:标签内后跟的控制标签行为的属性,其后所写的为属性值,简单举例:
<h1 align="right">I Love You</h1>
借此实现标题文字右对齐,其中,align
为属性,right
为属性值
- 由此,HTML基本语法格式为:
<标签 属性="值" 属性="值">被标记的内容</标签>
Xpath 解析_XML 概念
- Xpath 解析:XML 解析器,用来提取XML 文档中的节点,Xpath 是在 XML 文档中搜索的一门语言。HTML 是 XML 的一个子集
- 基础概念:
<book>
<id>1</id>
<name>野花遍地香</name>
<price>1.23<price>
<author>'
<nick>周大强</nick>
<nick>周芷若</nick>
</author>
</book>
在上述 html 中:
book
,id
,name
,price
等都被称为节点id
,name
,price
,author
被称为book
的子节点,book
被称为他们的父节点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="""
<div class='jay'><span id='1'>雷军</span></div>
<div class='jj'><span id='2'>李彦宏</span></div>
<div class='jolin'><span id='3'>张小龙</span></div>
<div class='sylar'><span id='4'>马云</span></div>
<div class='tory'><span id='5'>马化腾</span></div>
"""
obj1 = re.compile("<div class='.*?'><span id='\\d+'>.*?</span></div>",re.S) #re.S作用:让点.能匹配换行符
#<div class='部分都一样,后面不一样部分.*?代替,匹配后jay双引号后部分一样,一直到id=后单引号后不同,
#用\\d或\\d+或者.*?代替,后同理,略
result = obj1.finditer(s)
for it in result:
print(it.group())
print()
#从全部文本信息中提取想要的信息(在上述代码中修改)
#在要提取的文本.*?等正则符号处,用小括号包住,小括号前写?P<组名> ,最后在遍历的组括号(60行)写入这个组名
obj1 = re.compile("<div class='.*?'><span id='\\d+'>(?P<gualudeng>.*?)</span></div>",re.S) #re.S作用:让点.能匹配换行符
#<div class='部分都一样,后面不一样部分.*?代替,匹配后jay双引号后部分一样,一直到id=后单引号后不同,
#用\\d或\\d+或者.*?代替,后同理,略
result = obj1.finditer(s)
for it in result:
print(it.group("gualudeng"))
print()
手刃豆瓣 top250
#数据在页面源代码中
#思路:拿到页面源代码,通过re正则提取我们想要的有效信息
from email import header
import requests,re,csv
url = "https://movie.douban.com/top250"
ua = "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"
resp = requests.get(url,headers=ua) #简单的提取源代码和反反爬
#print(resp.text) #检查页面源码
page_content = resp.text #保存源代码至变量
#解析数据
#正则表达式定位,建议找需要数据的上几层标签做定位
#<li>为上层标签,换行时的空白可能是换行可能是空格,使用.*?表示,继续匹配到下一行,后面多行都用.*?匹配,直接找到需要的title,在需要部分单独列组(),补充后面的截止部分(此处截止至</span>处),后略
obj = re.compile('<li>.*?<div class="item">.*?<span class="title">(?P<title>.*?)</span>.*?<p class="">.*?<br>(?P<year>.*?) .*?<span class="rating_num" property="v:average">(?P<score>.*?)</span>',re.S) #编写正则方法
#使用finditer进行正则筛选
result = obj.finditer(page_content)
#遍历result,得到数据
for it in result:
print("\\n电影名:",it.group("title"),"\\n年份:",it.group("year").strip(),"\\n评分:",it.group("score")) #group中的名字均为正则中的组名, .strip()为去除空白(空格)
#将数据存入文件,建议存储为csv格式。引入csv模块,.csv文件默认以逗号进行数据分割
f = open("data.csv",mode="w",encoding="utf-8") #打开文件data.csv,没有文件自动创建,模式为r写入,打开格式为utf-8
csvwriter = csv.writer(f) #创建csvwriter,写入数据时写入f文件,注意写入数据格式应为字典
result = obj.finditer(page_content) #同18行
for it in result:
dic= it.groupdict() #创建字典,将上述20-21行数据整理进字典
dic["year"] = dic["year"].strip() #单独处理需要去掉空格的year组
csvwriter.writerow(dic.values()) #writerow为写入一行函数,括号()内为写入数据,写入的为字典的数据.values()
f.close() #关闭文件
print("over!")
#目前完成了top25的整理,而翻页数据只需要修改url后的参数即可,比如第二页url为https://movie.douban.com/top250?start=25&filter=
#由此得第一页参数start=0,第三页start=50,所以输出top250排行榜,可以此为方向研究
参考源代码:
屠戮盗版天堂电影信息
盗版天堂
补充 html 中 a 标签超链接知识
"""
1、确认数据在页面源码中,定位到2022必看热片
2、从2022必看热片中提取到子页面链接地址
3、请求子页面的链接地址,拿到想要的下载地址
"""
- 实际操作
import requests,re
main_url = "https://dytt89.com/" #主界面url
child_url_list = []
#原老版网站存在https加密,requests模块也有安全验证,所以会报错,可以使用verify=False关闭安全验证来解决,运行时最上部的警告意为“请求没有进行安全验证”。新版网站已取消
resp = requests.get(main_url , verify=False) #verify=False关闭安全验证
resp.encoding = "gb2312" #指定字符集编码
#print(resp.text) #输出乱码,需要重新编码,网页编码格式通常在源码charset处会写明,找到后补充上一行代码,更改默认编码
#定位提取ul里面的li
obj1 = re.compile('2022必看热片.*?<ul>(?P<ul>.*?)</ul>',re.S) #提取需要的部分
obj2 = re.compile("<a href='(?P<href>.*?)'",re.S) #提取a标签中的url链接
#开始筛选提取
result1 = obj1.finditer(resp.text) #第一次提取板块源码部分
for it in result1:
ul = it.group("ul") #存入ul
#print(ul) #检验输出
#html知识补充:在html中,a标签表示超链接,如:<a href='url'>周杰伦</a>,网页上显示周杰伦的超链接,跳转地址为href=后的url
#提取子页面链接(href后url)
result2 = obj2.finditer(ul) #第二次从板块源码部分提取url,但提取的url为参数,需要与main_url拼接
for itt in result2:
add = itt.group("href") #存入add
#print(add) #检验输出
child_url = main_url + add.strip("/") #拼接url,使用strip除去拼接处多余的一个/符号
#print(child_url) #检验输出
child_url_list.append(child_url) #将网址保存进列表里(注意空列表已经提前定义)
#提取子页面内容
obj3 = re.compile('◎片 名(?P<movie>.*?)<br />.*?<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<download>.*?)">',re.S)
for url in child_url_list:
child_resp = requests.get(url) #操作基本同上
child_resp.encoding = "gb2312"
#下两行仅为测试使用
#print(child_resp.text)
#break
result3 = obj3.search(child_resp.text)
print(result3.group("movie"))
print(result3.group("download"))
参考源代码:
python 实现 Bs4 解析
Python 的 bs4 模块使用
python 的 bs4
模块为第三方模块,需要先安装,安装 cmd 语法如下:
pip install bs4
抓取示例:北京新发地菜价(已失效,仅可参考)
北京新发地地址(已重构)
注:页面重构,下示例代码仅可参考,无法运行,网站改为浏览器渲染,使用 POST 请求
# 页面源代码中能找到数据,所以直接爬取,后使用bs4提取数据即可
import requests
import csv
from bs4 import BeautifulSoup
url = "http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml"
resp = requests.get(url)
# print(resp.text) #测试
# 准备需要写入的文件
f = open("菜价.csv", mode="w")
csvwriter = csv.writer(f)
# 解析数据,把页面源代码交给beautiful soup处理,生成bs4的对象
page = BeautifulSoup(resp.text, "html.parser") # 括号第二个参数指定html解析器
# 从bs4对象查找数据(find / find_all(标签 属性="值"))
# 查找内容。由于class是python关键字,所以写class_代替
table = page.find("table", class_="hq_table")
# print(table) #测试
# 得到的是表格,表格内每一行为tr标签,每一行内每列为td标签
# 再次筛选tr,拿到所有数据行,做切片,从1行开始切,去除0行的表头
trs = table.find_all("tr")[1:]
for tr in trs: # 每一行的数据进行遍历
tds = tr.find_all("td") # 拿到每行中的所有td
name = tds[0].text # .text表示拿到被标签标记的内容
low = tds[1].text
avg = tds[2].text
high = tds[3].text
kind = tds[4].text
set = tds[5].text
date = tds[6].text
# print(name, low, avg, high, kind, set, date) #输出测试
csvwriter.writerow([name, low, avg, high, kind, set, date]) # 写入文件,需要列表
f.close()
print("over")
参考源代码: