# pythonSpider **Repository Path**: zengshijun/pythonSpider ## Basic Information - **Project Name**: pythonSpider - **Description**: 学习路飞学城IT的代码练习 - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2023-03-11 - **Last Updated**: 2023-03-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 教程简介 + 这个是在B站的一个在线课程, + up主:**路飞学城IT** + 一共*100*P + 学习内容包括:`http`、`request`、`正则`、`bs4`、`xpath`、`异步`、`协程`、`aiohttp`、`selenium`、`scrapy`、`CrawlSpider`、`asyncio`... + 课程名称:2020年Python爬虫全套课程(学完可做项目) + 课程地址:[https://www.bilibili.com/video/BV1Yh411o7Sz](https://www.bilibili.com/video/BV1Yh411o7Sz) + 学习课件:[https://book.apeland.cn/details/68/](https://book.apeland.cn/details/68/) + 路飞学诚 爬虫的课件 链接:[https://pan.baidu.com/s/1KWBu4oswrPZMD9K_4lFe8A](https://pan.baidu.com/s/1KWBu4oswrPZMD9K_4lFe8A) 密码:`91pl` + 目前学习到`23P` # 1、爬虫简介 ## 1.1、爬虫分类 ### 1.1.1、通用爬虫 + 就是获取一个url,把整个页面==所有内容==爬取下来 ### 1.1.2、聚焦爬虫 + 聚焦爬虫是建立在通用爬虫上面的 + 先把整个页面爬取下来,取出自己想要的==部分内容== ### 1.1.3、增量式爬虫 + 增量爬虫是检测网站的更新情况 ## 1.2、反爬 ### 1.2.1、robots.txt协议 + 规定了网站那个部分的内容允许爬取。 + ==非强制==,可遵守可不遵守(*君子协议*) > 可以直接打开robots.txt查看 > > [这是B站的一个robots.txt https://www.bilibili.com/robots.txt](https://www.bilibili.com/robots.txt) 下面是B站的robots协议 ![1604542571621](路飞学城IT_python爬虫学习.assets/1604542571621.png "bilibili robots协议") # 2、http和https协议 ## 2.1 http协议 ### 2.1.1、 请求头信息 #### 2.1.1.1、User-Agent + 请求载体的身份标识(包括系统版本,浏览器版本) #### 2.1.1.2、Connection + 请求完毕之后,是断开连接还是保持连接 ### 2.1.2、响应头信息 #### 2.1.2.1、Content-Type + 服务器响应之后返回的数据类型 ## 2.2 https协议 ### 2.2.1、与http的区别 + 唯一区别是安全与不安全, + ***https***是安全的***http***是不安全的 ### 2.2.2、加密的方式 #### 2.2.2.1、对称秘钥加密 + 在客户端进行加锁, + 把秘钥和加密信息一起传送个服务器, + 然后服务器用传送过来的秘钥解锁加密信息 ![1604544418860](路飞学城IT_python爬虫学习.assets/1604544418860.png "对称秘钥流程图") > 弊端:如果使用软件拦截,把秘钥和加密信息拦截到,也是可以进行解锁的。 #### 2.2.2.2、非对称秘钥加密 1. 服务器把公钥给客户端 2. 客户端用公钥对文件加密,然后 把密文发给服务器 3. 服务器用私钥进行解密 ![1604546107091](路飞学城IT_python爬虫学习.assets/1604546107091.png "非对称秘钥加密流程图") > * 公钥和私钥[https://zhuanlan.zhihu.com/p/31477508](https://zhuanlan.zhihu.com/p/31477508) > * 理论上只要私钥不泄露就有加密效果 > > ***缺点*** > > 1. 效率比较低,处理更加复杂 > 2. 还是会有安全隐患 > 1. 如果在服务器发送公钥的时候被拦截 > 2. 拦截之后串改公钥,然后发给客户端 > 3. 拦截密文,用自己串改之后的私钥解密,就可以得到内容 #### 2.2.2.3、证书秘钥加密 `https采用的加密方式` ![1604548252345](路飞学城IT_python爬虫学习.assets/1604548252345.png "证书秘钥加密原理") 1. 服务器向认证机构发送公钥, 2. 认证机构对公钥进行==数字签名==,并把秘钥和数字签名绑定在一起形成==数字证书== 3. 服务器发送数字证书给客户端,客户端通过验证==数字签名==判断证书的真伪 4. 信息无误,客户端就可以使用公钥进行加密,把密文发送给服务器 5. 服务器使用私钥解密,读取文件 ![1604560786953](路飞学城IT_python爬虫学习.assets/1604560786953.png "服务器向证书认证机构获取数字证书的过程") # 3、requests模块 > * requests模块包括两个模块 > * ***urllib*** (比较古老,不常用) > * ***requests*** (现在用这个比较多) > * 用来模拟浏览器发送请求。 ## 3.1、环境安装 在终端进行环境安装 ```shell pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple 或者如果装有Anaconda可以使用命令conda conda install requests ``` ## 3.2、实战编码 ### 3.2.1、爬取搜狗首页的数据 ```python ## 导入requests import requests def spider1(): ## 使用requests的get请求方法,请求搜狗引擎的首页 response = requests.get(url="https://www.sogou.com/") ## 打印输出首页的内容 print(response.text) ## 保存为sogou.html with open("./sogou.html","w+",encoding="utf-8") as f: ## 写入搜狗首页的内容 f.write(response.text) ## 关闭文件 f.close() if __name__ == '__main__': ## 使用spider1这个方法 spider1() ``` > * 在*3.requests*文件夹里面创建*souGou.py* > * 爬取内容:爬取搜狗首页数据,并保存为html ## 3.3、实战巩固 ### 3.3.1、爬取指定词对应的搜索结果 ```python import requests def spider1(): ## 输入要检索的内容 words = input("输入要搜索的内容:") ## url地址 url = "https://www.sogou.com/web" ## 传递get请求的参数 param = { "query":words } ## 头部信息,User-Agent伪装 headers = { "User-Agent":'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' } ## 请求,把url、参数、和头部信息写进去 response = requests.get(url=url,params=param,headers=headers) ## 输出请求的url地址 print(response.url) ## 保存该词数据的html with open(words+".html","w+",encoding="utf-8") as f: f.write(response.text) f.close() if __name__ == '__main__': spider1() ``` > 涉及到的新内容 > > * ***User-Agent***伪装,添加请求头部信息。 > * ***params***参数传递,用来传递get请求的参数,当然也可以用字符串拼接的方式传参。 ### 3.3.2、破解百度翻译 ```python import requests import json def spider1(): words = input("请输入要查询的单词:") ## 需要post请求的地址 url = 'https://fanyi.baidu.com/sug' ## post需要传递的参数 data={ "kw":words } ## 请求头,User-Agent伪装 headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } ## 进行post请求,输入地址,表单参数以及请求头 response = requests.post(url=url,data=data,headers=headers) ## requests读取json的两种方法: ## 方法1 使用json库,把str转换字典 # data = json.loads(response.text)['data'] ## 方法2 使用requests自带的方法,把json转换成字典 data = response.json() print(data) ## 保存json有两个方法: ## 方法1,把字典转换成json然后使用常规方法保存 ''' sava = json.dumps(data) with open(words+".json","w+",encoding="utf-8")as f: f.write(sava) f.close() ''' ## 方法2 方法和上面类似一个是dumps一个是dump fp = open(words+".json","w+",encoding="utf-8") json.dump(data,fp=fp,ensure_ascii=False) if __name__ == '__main__': spider1(); ``` > * 输入一个单词,查询并保存这个单词的json文件。 > * 使用了***post***请求以及***json库***的使用 ### 3.3.3、豆瓣电影爬取 #### 3.3.3.1、pyCharm正则替换 ``` ## 把匹配上的替换成下面的格式 二 .使用快捷键:Ctrl+R 调出正则匹配工具栏 三.按照下图步骤操作: 1. 选中需要快速加引号的区域 2. 填写正则匹配表达式, (.*?):(.*) '$1':'$2', 3. 勾选三个选项 ``` > [点击查看网址](https://blog.csdn.net/AinUser/article/details/83828069?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-10-83828069.nonecase&utm_term=%E5%AD%97%E5%85%B8%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%8A%A0%E5%BC%95%E5%8F%B7&spm=1000.2123.3001.4430) ![img](路飞学城IT_python爬虫学习.assets/20180714152120916.png) #### 3.3.3.2、代码 ```python import requests import json def spider1(): ## 地址固定是戏剧排行榜里面的 url = 'https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action=' num = -20 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } ## 设置爬取多少页 page = 2 ## 新建一个几个,把所有爬取到的json都放在这个集合里面,然后一次性再全部写出 result=[] ## for循环请求不同页面 for i in range(0, page): ## 修改读取数据起始位置,因为limit=20所以说明了一页20条记录 num += 20 param = { "start": num, "limit": 20 } ## 发送get请求,把要传递给参数param,User-Agent放进去 response = requests.get(url=url,params=param,headers=headers) ## 输出当前爬取的url地址 print(response.url) ## 把当前爬取页面的数据放进resulet这个集合里面 result += response.json() ## 新建文件夹,把resulet写出 fp = open("./豆瓣电影.json","w+",encoding="utf-8") json.dump(result,fp=fp,ensure_ascii=False) print if __name__ == '__main__': spider1() ``` > * [豆瓣电影](https://movie.douban.com/) > * 使用***get请求*** ### 3.3.4、作业(爬取肯定及的餐厅信息) > 肯德基官方:[http://www.kfc.com.cn/kfccda/index.aspx](http://www.kfc.com.cn/kfccda/index.aspx) ```python import requests import math import json def spider1(): url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword' location = input("请输入你要查询的位置:") ''' 需要表单提交的数据, - keyword表示搜索的关键字(也就是地址) - pageIndex表示页面索引,第几页 - pageSize表示页面数据数量,有几条数据 ''' data ={ 'cname': '', 'pid': '', 'keyword': location, 'pageIndex': '1', 'pageSize': '10', } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } ## 使用post请求,输入url表单需要的数据以及请求头。 response = requests.post(url=url,data=data,headers=headers) ## 转换成json格式 da = response.json() ## 所查询的一个有多少个餐厅 num = da['Table'][0]['rowcount'] print("你搜索的:"+location+"一共有:"+str(num)+"个餐厅") ## 向上取整,计算一共有多少页 page = math.ceil(num/10) print("一共有:"+str(page)+"页") ## 新建一个集合,把所有json数据放进这个集合里面,然后写出 result = [] for i in range(0,page): ## 修改表单pageIndex(代表第几页)的数据,这个表示重新开始爬取数据,从第一页开始爬取 data["pageIndex"]=i+1 print("当前正在爬取第" + str(data["pageIndex"]) + "页") ## 发起post请求 response = requests.post(url=url, data=data,headers=headers) ## 获取到数据,把数据放进result集合里面 result += response.json()['Table1'] print("爬取第"+str(data["pageIndex"])+"页面完毕") print("写入数据...") fp = open(location+".json","w+",encoding="utf-8") json.dump(result,fp=fp,ensure_ascii=False) print("写入完毕,爬虫结束!") if __name__ == '__main__': spider1() ``` ![1604625581041](路飞学城IT_python爬虫学习.assets/1604625581041.png) > - 这是一个作业,爬取肯德基餐厅的json文件并保存 > - 先爬取第一页的页面, > - 然后可以获得一共有多少个数据, > - 然后再计算有多少页 > - 计算多少页之后就可以写循环,爬取所有数据了。 ### 3.3.5、药监局总局相关数据爬取 ```python import requests import json def spider1(): ## 第一个url用来获取id,和最大页数 url1 = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList' ## 第二个url,获取所需要的json数据 url2 = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } ## 第一个urlpost请求需要的数据,page表示第几页,pageSize表示页面有多少条数据 data1 = { 'on': 'true', 'page': '2', 'pageSize': '15', 'productName': '', 'conditionType': '1', 'applyname': '', 'applysn': '', } ## 对第一个url进行post请求,用来读取一共有多少页 response = requests.post(url=url1,data=data1,headers=headers).json() page = response['pageCount'] print("**********一共有:"+str(page)+"页***********") ## 所有数据将会保存到这个集合统一写出 result = [] ## 一个页面循环一个,page就表示循环所有页面 for i in range(0,page): ## 修改第一个表单的page的内容,表示第几页 data1['page']=i+1 print("**********当前正在爬取第"+str(data1['page'])+"页**********") ## 重新请求,获取当前页面的所有id值 response = requests.post(url=url1, data=data1, headers=headers).json() for resp in response['list']: ## 第二个url需要提交的表单,每一个id提交一次,得到相应的json数据 data2 = { "id":resp['ID'] } da2 = requests.post(url=url2,data=data2).json() ## 把得到的数据加入到result这个列表里面 result.append(da2) ## 因为测试原因,所以不需要爬取这么多,写一个跳出循环语句 page_limit = 10 if (data1['page'] == page_limit): print("**********一个爬取"+str(page_limit)+"页。需要爬取更多请修改page_limit这个变量!**********") break; ## 保存数据 print("********正在保存数据....********") fp = open("./药监局/化妆品.json","w+",encoding="utf-8") json.dump(result,fp=fp,ensure_ascii=False) print("保存完毕,爬取完成!") if __name__ == '__main__': spider1() ``` ![1604633957757](路飞学城IT_python爬虫学习.assets/1604633957757.png) > - 需要post请求两个页面,第一个url用来获取页的数量,以及id号码 > - 第二个url是第一个url获取的id请求获取数据。 # 4、数据解析 ## 4.1、正则解析 ### 4.1.1、正则基础 ![img](路飞学城IT_python爬虫学习.assets/snip20190415_21.png) ### 4.1.2、糗事百科热图图片爬取 ```python import requests import re import os headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } def spider1(): url = 'https://www.qiushibaike.com/imgrank/page/' ## 需要爬取多少页 page = 2 ## 循环请求每一个页面 for i in range(0,page): ## 拼接url page_url = url + str(i+1) ## 请求当前页面的数据 reponse = requests.get(url=page_url,headers=headers) ## 使用正则表达式获取当前是第几页 current = re.findall('\s(.*?)\s',reponse.text)[0] print("********当前正在爬取"+current+"页********") ## 使用sava_page保存当前页面的所有图片 sava_page(reponse,current) print("一共爬取"+str(page)+"页,如果需要爬取更多,请求page这个变量,目前最多13页。") print("爬取完毕") ''' 函数名:sava_page 需要参数:responde(一个页面的响应数据),current(当前爬取的页面) 方法功能:可以保存一个页面的所有图片(25张) ''' def sava_page(reponse,current): ## 创建保存图片的文件夹,如果“糗事image”文件夹不存在则创建 if not os.path.exists('./糗事image'): print("“糗事image”文件夹不存在,正在创建文件夹...") os.mkdir('./糗事image') print("创建成功!") ## 获取当前页面的所有图片url地址,以及把alt获取下来当做图片名字 image_url_list = re.findall('(.*?)',reponse.text) ## 循环请求每张图片url,并保存 for i in image_url_list: ## 拼接图片url image_url = "http:" + i[0] ## 文件名字 name = i[1] ## 请求图片 image = requests.get(url=image_url, headers=headers).content ## 文件名全称加地址 fileName = "./糗事image/" + name + ".jpg" ## 保存文件 with open(fileName, "wb") as fp: fp.write(image) print("*****写入第"+current+"完成****") if __name__ == '__main__': spider1() ``` ![1604655712849](路飞学城IT_python爬虫学习.assets/1604655712849.png) > * 爬取糗事百科热图里面的所有照片, >* 先获取第一页的数据,然后通过正则提取所有的图片url地址,然后保存图片 > ***新知识*** > * 使用os库判断文件是否存在,如果不存在则创建新的文件夹 ## 4.2、bs4解析 ### 4.2.1、环境安装 #### 4.2.1.1、直接使用-i或者conda的方式安装 ```shell pip install bs4 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple 或者如果装有Anaconda可以使用命令conda conda install bs4 conda install lxml ``` #### 4.2.1.2、使用配置环境的方式安装 ``` - 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等 - windows (1)打开文件资源管理器(文件夹地址栏中) (2)地址栏上面输入 %appdata% (3)在这里面新建一个文件夹 pip (4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可 [global] timeout = 6000 index-url = https://mirrors.aliyun.com/pypi/simple/ trusted-host = mirrors.aliyun.com - linux (1)cd ~ (2)mkdir ~/.pip (3)vi ~/.pip/pip.conf (4)编辑内容,和windows一模一样 - 需要安装:pip install bs4 bs4在使用时候需要一个第三方库,把这个库也安装一下 pip install lxml ``` ### 4.2.2、bs4本地的html数据读取 ```python from bs4 import BeautifulSoup if __name__ == '__main__': ## 读取本地的一个html文件 fp = open('../3.requests/sogou.html','r',encoding="utf-8") ## 使用BeautifulSoup和lxml格式化文件 soup = BeautifulSoup(fp,'lxml') ## 打印输出文件 print(soup) ``` > - 读取本地和网络的都是差不多,都是把一个string转换成格式化的html > - 读取底本的方法不常用 ### 4.2.3、bs4网络上的html数据格式化 ```python from bs4 import BeautifulSoup import requests if __name__ == '__main__': url = 'https://www.qiushibaike.com/imgrank/' ## 发起一个get请求,用来获取一个html文件 response = requests.get(url=url) ## 或获取到的数据使用BeautifulSoup和lxml格式化 soup = BeautifulSoup(response.text,'lxml') ## 读取数据,title标签下的内容 print(soup.title.text) ``` > * 这是互联网上读取的html ### 4.2.4、bs4的方法的属性 ``` 使用流程: - 导包:from bs4 import BeautifulSoup - 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容 (1)转化本地文件: - soup = BeautifulSoup(open('本地文件'), 'lxml') (2)转化网络文件: - soup = BeautifulSoup('字符串类型或者字节类型', 'lxml') (3)打印soup对象显示内容为html文件中的内容 基础巩固: (1)根据标签名查找 - soup.a 只能找到第一个符合要求的标签 (2)获取属性 - soup.a.attrs 获取a所有的属性和属性值,返回一个字典 - soup.a.attrs['href'] 获取href属性 - soup.a['href'] 也可简写为这种形式 (3)获取内容 - soup.a.string - soup.a.text - soup.a.get_text() 【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容 (4)find:找到第一个符合要求的标签 - soup.find('a') 找到第一个符合要求的 - soup.find('a', title="xxx") - soup.find('a', alt="xxx") - soup.find('a', class_="xxx") - soup.find('a', id="xxx") (5)find_all:找到所有符合要求的标签 - soup.find_all('a') - soup.find_all(['a','b']) 找到所有的a和b标签 - soup.find_all('a', limit=2) 限制前两个 (6)根据选择器选择指定的内容 select:soup.select('#feng') - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 - 层级选择器: div .dudu #lala .meme .xixi 下面好多级 div > p > a > .lala 只能是下面一级 【注意】select选择器返回永远是列表,需要通过下标提取指定的对象 ``` ![1604663169037](路飞学城IT_python爬虫学习.assets/1604663169037.png) ### 4.2.5、bs4案例(爬取三国演义) ```python import requests from bs4 import BeautifulSoup import os headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } def spider1(): ## 三国演义页面的url地址,用来获取章节的名字和章节详细内容的url url = 'https://www.shicimingju.com/book/sanguoyanyi.html' ## 发起get请求,获取数据 response = requests.get(url=url,headers=headers).text ## 使用beautifulSoup格式化内容 soup = BeautifulSoup(response,'lxml') ## 使用选择器获取标题和详细内容url for i in soup.select('div .book-mulu > ul > li > a'): title = i.text href = "https://www.shicimingju.com"+i['href'] page2(title, href) print("*****正在爬取:",title,"*******") print("全部下载完毕") ''' 参数:title(标题,用来命名章节),href(章节url地址,用来请求获取数据) 功能:请求第二个页面,下载保存第二个页面的文本内容 ''' def page2(title,href): ## 创建文件夹保存文本 if not os.path.exists("./三国演义"): print("没有“三国演义“这个文件夹,正在创建...") os.mkdir("./三国演义") print("创建成功!!") ## 获取章节里面的数据 response = requests.get(url=href,headers=headers).text soup = BeautifulSoup(response,'lxml') p = soup.find_all('div',class_='chapter_content')[0].find_all('p') ## 下载保存文本 with open("./三国演义/"+title+".txt","w+",encoding="utf-8") as fp: for i in p: fp.write(" "+i.text+"\n") print("下载保存完毕") if __name__ == '__main__': spider1() ``` ![1604666824484](路飞学城IT_python爬虫学习.assets/1604666824484.png) > - 使用bs4爬取数据。 > - 先获取所有的章节,和对应的url地址 > - 使用获取得到的url地址,获取第二个页面的内容, > - 使用bs4得到我们需要的文本,然后保存。 ## 4.3、xpath解析 ### 4.3.1、环境安装 ```shell pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple conda install lxml ``` ### 4.3.2、etree本地读取HTML文件 ```python from lxml import etree if __name__ == '__main__': ## 读取本地文件,使用etree的parse方法,第一个参数的路径文件名,第二个参数是规定 tree = etree.parse('../3.requests/sogou.html',etree.HTMLParser()) ## 读取xpath的title标签 lists = tree.xpath('//title') ## 打印输出所有内容 for i in lists: ## 输出title标签下的文本 print("标签名为:",i.tag) print("标签下的文本为:",i.text) ``` ![1604716536482](路飞学城IT_python爬虫学习.assets/1604716536482.png) ### 4.3.3、etree互联网上读取HTML ```python import requests from lxml import etree if __name__ == '__main__': url = 'https://www.qiushibaike.com/imgrank/' ## get一个页面 response = requests.get(url=url).text ## 把数据格式化为HTML格式 html = etree.HTML(response) ## 使用xpath获取想要的数据,这里是热图中发表的文字 html_text = html.xpath('//div[@class="content"]/span') ## 使用循环取出列表的内容 for i in html_text: ## 输出文本,并把换行符去掉 print(i.text.replace("\n","")) ``` ![1604717047891](路飞学城IT_python爬虫学习.assets/1604717047891.png) > - 在互联网中格式化和在本地格式化类似, > - 不过在本地的时候要用到***HTMLparser()***其他的就基本一样了。 ### 4.3.4、常用xpath表达式 ```python 属性定位: #找到class属性值为song的div标签 //div[@class="song"] 层级&索引定位: #找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a //div[@class="tang"]/ul/li[2]/a 逻辑运算: #找到href属性值为空且class属性值为du的a标签(还有一个or) //a[@href="" and @class="du"] 模糊匹配: //div[contains(@class, "ng")] //div[starts-with(@class, "ta")] 取文本: # /表示获取某个标签下的文本内容 # //表示获取某个标签下的文本内容和所有子标签下的文本内容 //div[@class="song"]/p[1]/text() //div[@class="tang"]//text() 取属性: //div[@class="tang"]//li[2]/a/@href ``` ```python import requests from lxml import etree if __name__ == '__main__': headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } url = 'https://blog.csdn.net/qq_35208583/article/details/89041912' response = requests.get(url=url,headers=headers).text ## 格式化 html = etree.HTML(response) ## 属性定位,取属性 content = html.xpath('//meta[@name="description"]/@content') for i in content: print(i) ``` > - csdn [lxml-etree的使用](https://blog.csdn.net/qq_35208583/article/details/89041912) ### 4.3.5、实战案列 #### 4.3.5.1、解析58二手房的相关数据 ``` import requests from lxml import etree def spider1(): url = 'https://zh.58.com/ershoufang/?PGTID=0d100000-0038-e6c2-63e8-b84e5ec27571&ClickID=1' ## 请求第一个页面,获取标题和详细页的url地址 response = requests.get(url=url,headers=headers).text ## 使用lxml.etree格式化 html = etree.HTML(response) ## 使用xpath获取内容 data = html.xpath('//h2[@class="title"]') ## 循环读取 for da in data: ## 使用xpath获取我们想要的标题 title = da.xpath('./a/text()')[0] ## 使用xpath获取我们想要的详细页url地址 url = da.xpath('./a/@href')[0] ## 因为有一半的url地址是没有https://的,所以我们需要做一个判断,加上https,不然会请求失败,因为他并不是一个完整的url地址 if "https://" not in url: url = "https:"+url print("当前正在爬取:",title,url) ## 进入detail方法处理 detail(title,url) break; ''' 参数:url(详细页的url),title(二手房的标题) 函数功能:使用url请求获取详细页的数据(后面引发同一个ip请求多次被方法,所以并没有写完。) ''' def detail(title,url): ## 使用get请求,获取详细页的内容 response = requests.get(url=url,headers=headers).text ## 使用etree.HTML格式化数据 html = etree.HTML(response) ## 使用xpath获取我们想要的文本内容 desc = html.xpath('//div[@class="general-item general-desc"]//text()') for i in desc: print(i.replace("\n","")) if __name__ == '__main__': headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" } spider1() ``` [https://www.bilibili.com/video/BV1Yh411o7Sz?p=23](https://www.bilibili.com/video/BV1Yh411o7Sz?p=23) # 把数据导入到mysql > - 使用pymsql > - 连接数据库格式:pymysql.connect(mysql地址,用户名,密码,数据库) > - `db = pymysql.connect("127.0.0.1","root","jian","test")` > - 创建执行语句,格式:变量名字 = 连接的赋值.cursor() > - `cursor = db.cursour()` > - 执行sql语句,格式:cursor.execute(sql语句,赋值(可以使列表,元祖,字典)) > - lists,tupe:`cursor.execute("select %s,%s from tableName",lists)` > - dict:`cursor.execute("select %(keyName)s from tableName",dicts)` > - 提交sql语句:`db.commit()` > - 失败回滚:`db.rollback()` > - 查询语句:`cursor.fetchall()`,需要配合执行语句 > - 关闭sql的连接:`db.close()` ```python import pymysql import requests def spider1(): ## 执行创表函数 create_table() url = 'https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action=' param = { "limit": 20, "start": 0 } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36" } response = requests.get(url=url, params=param, headers=headers) for i in response.json(): ''' 数据格式:['憨豆先生精选辑', 'https://movie.douban.com/subject/5133063/', "['喜剧']"] ''' lists = [i['title'], i['url'], str(i['types'])] print(lists) ## 插入数据 insert_table(lists) ''' 传递参数:无 功能:删除movies表。创建movies表。 ''' def create_table(): ## 连接数据库 db = pymysql.connect(host="127.0.0.1",user="root",password="jian",database="test",port=3306) ## 创建执行语句 cursor = db.cursor() sql_DropTable = "drop table if exists movies" ## 执行sql语句 cursor.execute(sql_DropTable) sql_CreateTable = """create table movies( id int primary key auto_increment, title varchar(50), url varchar(200), type varchar(150) )""" cursor.execute(sql_CreateTable) ## 提交sql语句 db.commit() print("创建movies表...") ## 关闭连接 db.close() ''' 传递参数:value(可以是一个三个字符串的列表,元祖) 功能:插入数据导入movies表。 ''' def insert_table(value): db = pymysql.connect(host="127.0.0.1",user="root",password="jian",database="test",port=3306,charset="utf8") cursor = db.cursor() sql = 'insert into movies(title,url,type) values(%s,%s,%s)' try: ## 执行sql语句,并传递参数 cursor.execute(sql, value) db.commit() print("插入数据成功...") except: db.rollback() print("插入失败") db.close() if __name__ == '__main__': spider1() ``` ![1605236909189](路飞学城IT_python爬虫学习.assets/1605236909189.png) 1605236918636