Python学习从0开始——项目一day01爬虫(二)

news/2024/5/21 15:29:26

Python学习从0开始——项目一day01爬虫`(二)`

  • 一、解析response数据
  • 二、json转换
  • 三、文件保存
  • 四、存储json对象
  • 五、完整代码

上一篇

一、解析response数据

在已经知道我们获取图片的最终URL存在于请求响应response中,下一步的重点就放在解析response。
首先给出现在的代码,以下代码暂时删除了图片写入的部分,在文章末尾会给出完整的爬虫代码。

#coding=utf-8
#!/usr/bin/python
# 导入requests库
import requests
# 导入文件操作库
import os
import bs4
from bs4 import BeautifulSoup
import sys
import importlib
importlib.reload(sys)
import re 
import json  
import urllib.parse# 给请求指定一个请求头来模拟chrome浏览器
global headers
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'}
# 爬图地址
mziTu = 'https://image.baidu.com/'
# 定义存储位置
global save_path
save_path ='./picture'# 创建文件夹
def createFile(file_path):if os.path.exists(file_path) is False:os.makedirs(file_path)# 切换路径至上面创建的文件夹os.chdir(file_path)# 下载文件
def download(page_no, file_path):global headersres_sub = requests.get(page_no, headers=headers)# 解析htmlsoup_sub = BeautifulSoup(res_sub.text, 'html.parser')# 获取页面的栏目地址all_a = soup_sub.find('div',id='bd-home-content-album').find_all('a',target='_blank')count = 0for a in all_a:count = count + 1if (count % 2) == 0:print("内页第几页:" + str(count))# 提取hrefhref = a.attrs['href']print("套图地址:" + href)res_sub_1 = requests.get(href, headers=headers)soup_sub_1 = BeautifulSoup(res_sub_1.text, 'html.parser')# 主方法
def main():res = requests.get(mziTu, headers=headers)# 使用自带的html.parser解析soup = BeautifulSoup(res.text, 'html.parser')# 创建文件夹createFile(save_path)file = save_pathcreateFile(file)print("开始执行")download(mziTu, file)if __name__ == '__main__':main()

在这里插入图片描述
在谷歌浏览器中,折叠代码块,快速定位到我们需要的<script>中,第十三个<script>是linkData所在标签,然后对代码继续修改,在53行添加如下内容:

			# 找到所有的<script>标签scripts = soup_sub_1.find_all('script')  # 第十三个是linkData所在标签script_content = BeautifulSoup(scripts[12].text, 'html.parser').text#打印值print(script_content)
#终端输出如下
!function(){    window.logid = "7865333382831002903";require.async(['albumsdetail:widget/ui/app/app'], function (app) {app.setPageInfo({word: '%E6%B8%90%E5%8F%98%E9%A3%8E%E6%A0%BC%E6%8F%92%E7%94%BB',hasResult: '1',albumTab: '%E8%AE%BE%E8%AE%A1%E7%B4%A0%E6%9D%90',setId: '409',title: '渐变风格插画',logo: 'https:\/\/emoji.cdn.bcebos.com\/yunque\/pc_vcg.png',coverUrl: 'https:\/\/t7.baidu.com\/it\/u=1819248061,230866778&fm=193&f=GIF',totalNum: '314',albumLinkRn: '30',linkData: '[{\x22pid\x22:144520,\x22width\x22:1200,\x22height\x22:562,\x22oriwidth\x22:1200,\x22oriheight\x22:562,\x22thumbnailUrl\x22:\x22https:\\\/\\\/t7.baidu.com\\\/it\\\/u=1819248061,230866778&fm=193&f=GIF\x22,\x22fromUrl\x22:\x22https:\\\/\\\/www.vcg.com\\\/creative\\\/1274231988\x22,\x22contSign\x22:\x221819248061,230866778\x22},{\x22pid\x22:144521,\x22width\x22:562,\x22height\x22:1000,\x22oriwidth\x22:562,\x22oriheight\x22:1000,\x22thumbnailUrl\x22:\x22https:\\\/\\\/t7.baidu.com\\\/it\\\/u=4036010509,3445021118&fm=193&f=GIF\x22,\x22fromUrl\x22:\x22https:\\\/\\\/www.vcg.com\\\/creative\\\/1147957933\x22,\x22contSign\x22:\x224036010509,3445021118\x22},……]

经过以上操作,成功的获取了linkData所在的<script>,下一步是获取linkData,我们通过正则来获取数据:

			# 使用正则表达式来查找linkData的值  link_data_pattern = r"linkData: '([^']*)'"  match = re.search(link_data_pattern, script_content)#查看输出print(match)   
#终端输入
python3 spider.py
#终端输出
<re.Match object; span=(605, 10524), match="linkData: '[{\\x22pid\\x22:144520,\\x22width\\x22>

这看起来并不符合我们的预期,我们期望的输出是linkData里的值。
这时,我们需要关注re.search(),其返回结果是一个捕获组,可以通过group()来获取每一组的数据,group(1) 表示获取第一个捕获组的内容。如果没有捕获组或者索引超出了捕获组的范围,group() 方法会抛出 IndexError 异常。
将输出替换为以下内容:

            print(match.group(1))     
#终端输入
python3 spider.py
#捕获组终端输出
[{\x22pid\x22:144520,\x22width\x22:1200,\x22height\x22:562,\x22oriwidth\x22:1200,\x22oriheight\x22:562,\x22thumbnailUrl\x22:\x22https:\\\/\\\/t7.baidu.com\\\/it\\\/u=1819248061,230866778&fm=193&f=GIF\x22,\x22fromUrl\x22:\x22https:\\\/\\\/www.vcg.com\\\/creative\\\/1274231988\x22,\x22contSign\x22:\x221819248061,230866778\x22},^}

下一步需要将捕获组的内容转为我们可以使用的数据。

二、json转换

为什么要将捕获组转换成json数据,什么情况下需要我们转为json数据?
看上方的捕获组输出,我们能明显的识别出这些数据具有统一的属性,直接截取字符串需要经过多次split或者replace,如果通过属性去获取值,会很便于我们操作。

#继续添加如下内容if match:  # 获取第一个捕获组的内容encoded_link_data = match.group(1)print(encoded_link_data) # 解析JSON对象  link_data_list = json.loads(encoded_link_data)  else:  print("未能找到linkData的值")

继续执行代码:

#终端输入
python3 spider.py
#终端输出
Traceback (most recent call last):File "/root/Python_02/Python/Day01/learn/spider.py", line 98, in <module>main()File "/root/Python_02/Python/Day01/learn/spider.py", line 95, in maindownload(mziTu, file)File "/root/Python_02/Python/Day01/learn/spider.py", line 70, in downloadlink_data_list = json.loads(encoded_link_data)  File "/usr/lib/python3.9/json/__init__.py", line 346, in loadsreturn _default_decoder.decode(s)File "/usr/lib/python3.9/json/decoder.py", line 337, in decodeobj, end = self.raw_decode(s, idx=_w(s, 0).end())File "/usr/lib/python3.9/json/decoder.py", line 353, in raw_decodeobj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 3 (char 2)

根据提示,我们知道现在无法解析为json数据。我们来看一下json的示例数据格式:

[{"name":"a","age":1
},{"name":"b","age":2
}]

显而易见,问题出在双引号上,那么下一步就需要将’\x22’字串替换为双引号。

#修改赋值
encoded_link_data = match.group(1).replace('\x22', '"')

此时,我们会发现,经过替换后仍旧报相同的错误,而且终端的输出的encoded_link_data 值和替换前没有区别。
为什么呢?
再来观察’\x22’,我们能发现它是一个转义序列,用于表示一个ASCII值为0x22的字符,即双引号,Python会通过转义序列将其解释为双引号,这就造成实际上是双引号替换双引号,故输出不变。而我们并不需要这种转义,我们需要Python将其解释为普通的字符串。

				#修改赋值,同时替换双引号和斜杠的转义 encoded_link_data = match.group(1).replace(r'\x22', '"').replace(r'\\\/', '/')#encoded_link_data = match.group(1).replace('\x22', '"')print(encoded_link_data) # 解析JSON对象  link_data_list = json.loads(encoded_link_data)  
#终端输入
cd Python/Day01/learn/
python3 spider.py
#输出,数据正常
[{"pid":144520,"width":1200,"height":562,"oriwidth":1200,"oriheight":562,"thumbnailUrl":"https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF","fromUrl":"https://www.vcg.com/creative/1274231988","contSign":"1819248061,230866778"},……]

三、文件保存

link_data_list现在已经存储了json数据,我们通过get方法获取对应的URL值,然后发送请求获取响应,继续添加以下内容:

#添加到if match:  里for item in link_data_list:  # 提取thumbnailUrl字段的值  thumbnail_url = item.get('thumbnailUrl')res_sub_2 = requests.get(thumbnail_url, headers=headers)soup_sub_2 = BeautifulSoup(res_sub_2.text, "html.parser")print("开始提取图片")file_name = thumbnail_urlf = open(file_name, 'ab')f.write(soup_sub_2)f.close()
#终端执行
python3 spider.py
#终端输出
开始提取图片
Traceback (most recent call last):File "/root/Python_02/Python/Day01/learn/spider.py", line 103, in <module>main()File "/root/Python_02/Python/Day01/learn/spider.py", line 100, in maindownload(mziTu, file)File "/root/Python_02/Python/Day01/learn/spider.py", line 79, in downloadf = open(file_name, 'ab')
FileNotFoundError: [Errno 2] No such file or directory: 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF'

使用初始代码的方法行不通,可能是因为没加文件类型,我们做一些小更改:

                    f = open(file_name+'.jpg', 'wb')

还报错,按照文件的输入输出来说我们的操作是正常的,符合流程的,问题会不会还是出现在文件名?
换个名字试一下。

                    f = open('a.jpg', 'ab')
#终端执行
python3 spider.py
#终端输出
Traceback (most recent call last):File "/root/Python_02/Python/Day01/learn/spider.py", line 105, in <module>main()File "/root/Python_02/Python/Day01/learn/spider.py", line 102, in maindownload(mziTu, file)File "/root/Python_02/Python/Day01/learn/spider.py", line 82, in downloadf.write(soup_sub_2)
TypeError: a bytes-like object is required, not 'BeautifulSoup'

虽然还是报错,但是报错内容变了,那现在可以确定,问题出现在文件名,对于Python来说,它本身并不直接限制文件名以"https"开头。但是,当我们试图创建、读取或操作一个文件时,实际上是在与底层的操作系统和文件系统交互。因此,真正限制使用"https"作为文件名开头的因素来自这些底层系统。知道原因后,我们就能解决问题了。
同时根据以上终端输出的内容,可以知道f.write()需要的是字节对象,而不是我们现在赋值的BeautifulSoup类型。继续修改:

            file_name = thumbnail_url.replace(r'https://t7.baidu.com/it/u=','')f = open(file_name+'.jpg', 'ab')f.write(res_sub_2.content)

这次正常执行了,来看一下输出的文件:
在这里插入图片描述

四、存储json对象

 #修改代码#添加行,存储数据,json数据以字符串形式存储,不是二进制    data = open('sava.txt', 'a')     if match:  # 获取第一个捕获组的内容# 提取匹配到的linkData字符串,字符替换时一定要使用r  encoded_link_data = match.group(1).replace(r'\x22', '"').replace(r'\\\/', '/')#encoded_link_data = match.group(1).replace('\x22', '"')#print(encoded_link_data) # 解析JSON对象  link_data_list = json.loads(encoded_link_data)  for item in link_data_list:  # 提取thumbnailUrl字段的值  thumbnail_url = item.get('thumbnailUrl')res_sub_2 = requests.get(thumbnail_url, headers=headers)soup_sub_2 = BeautifulSoup(res_sub_2.text, "html.parser")print("开始提取图片")file_name = thumbnail_url#    f = open(file_name, 'ab')                    #    f = open(file_name+'.jpg', 'ab')#    f = open('a.jpg', 'ab')                file_name = thumbnail_url.replace(r'https://t7.baidu.com/it/u=','')f = open(file_name+'.jpg', 'ab')#    f.write(soup_sub_2)f.write(res_sub_2.content)
#添加,写入,json转字符串写入data.write(json.dumps(item)+'\n')f.close()else:  print("未能找到linkData的值")
#关闭输入data.close()

在这里插入图片描述

至此,完整的一次爬虫结束。

五、完整代码

代码仅供参考学习使用。

#coding=utf-8
#!/usr/bin/python
# 导入requests库
import requests
# 导入文件操作库
import os
import bs4
from bs4 import BeautifulSoup
import sys
import importlib
importlib.reload(sys)
import re 
import json  
import urllib.parse# 给请求指定一个请求头来模拟chrome浏览器
global headers
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'}
# 爬图地址
mziTu = 'https://image.baidu.com/'
# 定义存储位置
global save_path
save_path ='./picture'# 创建文件夹
def createFile(file_path):if os.path.exists(file_path) is False:os.makedirs(file_path)# 切换路径至上面创建的文件夹os.chdir(file_path)# 下载文件
def download(page_no, file_path):global headersres_sub = requests.get(page_no, headers=headers)# 解析htmlsoup_sub = BeautifulSoup(res_sub.text, 'html.parser')# 获取页面的栏目地址all_a = soup_sub.find('div',id='bd-home-content-album').find_all('a',target='_blank')count = 0for a in all_a:count = count + 1if (count % 2) == 0:print("内页第几页:" + str(count))# 提取hrefhref = a.attrs['href']print("套图地址:" + href)res_sub_1 = requests.get(href, headers=headers)soup_sub_1 = BeautifulSoup(res_sub_1.text, 'html.parser')            # 找到所有的<script>标签scripts = soup_sub_1.find_all('script')  # 第十三个是linkData所在标签script_content = BeautifulSoup(scripts[12].text, 'html.parser').text#print(script_content)# 使用正则表达式来查找linkData的值  link_data_pattern = r"linkData: '([^']*)'"  match = re.search(link_data_pattern, script_content) #print(match)   #print(match.group(1))     data = open('sava.txt', 'a')     if match:  # 获取第一个捕获组的内容# 提取匹配到的linkData字符串,字符替换时一定要使用r  encoded_link_data = match.group(1).replace(r'\x22', '"').replace(r'\\\/', '/')#encoded_link_data = match.group(1).replace('\x22', '"')#print(encoded_link_data) # 解析JSON对象  link_data_list = json.loads(encoded_link_data)  for item in link_data_list:  # 提取thumbnailUrl字段的值  thumbnail_url = item.get('thumbnailUrl')res_sub_2 = requests.get(thumbnail_url, headers=headers)soup_sub_2 = BeautifulSoup(res_sub_2.text, "html.parser")print("开始提取图片")file_name = thumbnail_url#    f = open(file_name, 'ab')                    #    f = open(file_name+'.jpg', 'ab')#    f = open('a.jpg', 'ab')                file_name = thumbnail_url.replace(r'https://t7.baidu.com/it/u=','')f = open(file_name+'.jpg', 'ab')#    f.write(soup_sub_2)f.write(res_sub_2.content)data.write(json.dumps(item)+'\n')f.close()else:  print("未能找到linkData的值")data.close()# 主方法
def main():res = requests.get(mziTu, headers=headers)# 使用自带的html.parser解析soup = BeautifulSoup(res.text, 'html.parser')# 创建文件夹createFile(save_path)file = save_pathcreateFile(file)print("开始执行")download(mziTu, file)if __name__ == '__main__':main()

本来打算继续写存入数据库相关内容,但是MySQL服务器启动要会员,就只加了写数据到文件里,后续可以通过文件导入到数据库,线上就算了。


http://www.mrgr.cn/p/36855334

相关文章

[lesson35]函数对象分析

函数对象分析 客户需求 编写一个函数 函数可以获得斐波那契数列每项的值每调用一次返回一个值函数可根据需要重复使用 存在的问题 函数一旦开始调用就无法重来 静态局部变量处于函数内部&#xff0c;外界无法改变函数为全局函数&#xff0c;是唯一的&#xff0c;无法多次独…

【YOLOv9】使用yolov9训练自己的数据集/验证 /推理 /参数分析

完胜V8的SOTA模型Yolov9(论文阅读笔记)内容 点击即可跳转 当今的YOLO系列武林盟主YOLOV9&#xff1a; YOLOv9的优秀表现&#xff1a; 环境&#xff1a; ubuntu20.04&#xff0c;无GPU&#xff0c;使用anaconda3创建的虚拟环境yolov9。 环境安装&#xff1a; conda create -n …

FlinkSQL 实时同步 MySQL

本文主要介绍了使用 FlinkSQL 实现 MySQL 数据的实时同步。准备工作MySQL 数据库(version: 5.7.25),注意,MySQL 数据库版本必须大于 5.6,否则不支持。开启 MySQL 的 log-bin: [mysqld] # Binary Logging. log-bin=mysql-bin server-id=1Flink (version : 1.15.4)添加 fli…

Linux基础|线程池Part.1|线程池的定义和运行逻辑

线程池的定义和运行逻辑 多线程的问题&#xff1a; 如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c;这样频繁创建线程就会大大降低系统的效率&#xff0c;因为频繁创建线程和销毁线程需要时间。 那么一个很自然的想法就出现了…

FlinkSQL 实时数据同步

准备工作MySQL 数据库(version: 5.7.25),注意,MySQL 数据库版本必须大于 5.6,否则不支持。开启 MySQL 的 log-bin: [mysqld] # Binary Logging. log-bin=mysql-bin server-id=1Flink (version : 1.15.4)添加 flink-connector-jdbc-1.15.4.jar 和 flink-sql-connector-mys…

(三)C++自制植物大战僵尸游戏项目结构说明

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、项目结构 打开项目后&#xff0c;在解决方案管理器中有五个项目&#xff0c;分别是libbox2d、libcocos2d、librecast、libSpine、PlantsVsZombies五个项目&#xff0c;除PlantsVsZombies外&#xff0c;其他四个…

FFmpeg: 自实现ijkplayer播放器--06封装打开和关闭stream

文章目录 流程图stream openstream close流程图 stream open 初始化SDL以允许⾳频输出;初始化帧Frame队列初始化包Packet队列初始化时钟Clock初始化音量创建解复用读取线程read_thread创建视频刷新线程video_refresh_threadint FFPlayer::stream_open(const char

3、Git之常用命令

3.1、速查表命令 作用git config --global user.name 用户名 设置用户签名(昵称)git config --global user.email 邮箱 设置用户签名(邮箱)git init 初始化本地库git status 查看本地库状态git add 文件名 添加到暂存区git commit -m "日志信息" 文件名 提交到本…

2024 EasyRecovery三分钟帮你恢复 电脑硬盘格式化

随着数字化时代的到来&#xff0c;我们的生活和工作中越来越依赖于电子设备。然而&#xff0c;电子设备中的数据丢失问题也随之而来。数据丢失可能是由各种原因引起的&#xff0c;如硬盘故障、病毒感染、误删除等。面对这种情况&#xff0c;一个高效、可靠的数据恢复工具变得尤…

codeforce #925 (div3) 题解

D. Divisible Pairs 给出数组 a a a&#xff0c;如果二元组 ( i , j ) (i,j) (i,j)满足 a i a j m o d x 0 & & a i − a j m o d y 0 a_i a_j mod x 0 \&\& a_i - a_j mod y 0 ai​aj​modx0&&ai​−aj​mody0&#xff0c;则beauty。其中 i &…

ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写

原文链接&#xff1a;ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601336&idx2&sn10329c394c1d9fb0a4cedec6782e7997&chksmfa820d5fcdf58449bdebaefedbc96eb8c01845e12db5f2d6d5da…

读天才与算法:人脑与AI的数学思维笔记01_洛夫莱斯测试

读天才与算法:人脑与AI的数学思维笔记01_洛夫莱斯测试1. 创造力 1.1. 创造力是一种原动力,它驱使人们产生新的、令人惊讶的、有价值的想法,并积极地将这些想法付诸实践 1.2. 创造出在表面上看似新的东西相对容易 1.3. 在遇到偶然间的创造性行…

计算机网络:数据链路层 - CSMA/CA协议

计算机网络&#xff1a;数据链路层 - CSMA/CA协议 CSMA/CA概述帧间间隔工作原理退避算法虚拟载波监听 CSMA/CA概述 讲解CSMA/CA之前&#xff0c;我们回顾一下CSMA/CD的三个特性&#xff1a; 多址接入MA&#xff1a;多个主机连接在一条总线上&#xff0c;竞争使用总线 载波监听…

面试题总结:HashMap底层原理

不仅仅是一道题&#xff0c;之后的某一天&#xff0c;它可能是破局的关键。 关于HashMap的知识点有哪些呢&#xff1f;分层次展示 1.基础知识&#xff1a; 存储键值对结构、底层数据结构、红黑树和链表 2.位运算与实现 位运算、put、get方法的实现 3.关于锁 segment锁和桶锁、线…

Opencv3.4+FFMpeg3.4+pkg-config交叉编译arm开发板

Ubuntu16.04 64位 FFmpeg3.4 OpenCv3.4 一、下载FFmpeg https://github.com/FFmpeg/FFmpeg 1.配置 ./configure --prefix/home/zeng/ffmpeg_install --enable-cross-compile --cross-prefixarm-linux-gnueabihf- --ccarm-linux-gnueabihf-gcc --target-oslinux --cpuco…

光电传感器的工作原理简介

光电传感器是一种利用光电效应将光信号转换为电信号的传感器。 工作原理 光照射&#xff1a;光电传感器通过光源&#xff08;如LED或激光&#xff09;照射在其表面。 光电转换&#xff1a;光线与传感器材料发生光电反应&#xff0c;产生电信号。这种转换过程涉及到光子与电子的…

关于虚拟机内存和JVM内存设置的思考

关于虚拟机内存和JVM内存设置的思考背景 最近有同事总问JVM的设置问题. 之前总结过不少. 但是感觉没法讲对方说服 当然了, 自己能力有限, 只能自说自话. 现在这个就是留存一个底稿. 希望能人能帮忙解释关于内存和CPU的观点 CPU的能力有上限. 一般情况下不建议让CPU处于高峰作业…

如何实现Windows RDP 远程桌面异地跨网连接

Windows RDP远程桌面的应用非常广泛。远程桌面协议(RDP)是一个多通道(multi-channel)的协议&#xff0c;让使用者(所在计算机称为用户端或本地计算机)连上提供微软终端机服务的计算机(称为服务端或远程计算机)。大部分的Windows版本都有用户端所需软件&#xff0c;有些其他操作…

Rust面试宝典第2题:逆序输出整数

题目 写一个方法&#xff0c;将一个整数逆序打印输出到控制台。注意&#xff1a;当输入的数字含有结尾的0时&#xff0c;输出不应带有前导的0。比如&#xff1a;123的逆序输出为321&#xff0c;8600的逆序输出为68&#xff0c;-609的逆序输出为-906。 解析 这道题本身并没有什么…

数字孪生与企业

数字孪生技术&#xff0c;简而言之&#xff0c;就是创造一个物理实体的数字双胞胎&#xff0c;在虚拟世界中精确模拟现实世界的行为、过程和系统。这种技术的核心在于&#xff0c;它允许我们在数字环境中实时地监控、分析和优化其物理对应物的性能和效率。数字孪生的应用场景极…