您现在的位置是: 网站首页 >网络爬虫 >Python网络爬虫 网络爬虫

【Python网络爬虫】01.爬虫原理,爬虫常用库入门练习

admin2018年10月5日 22:18 Python | 爬虫 308人已围观

Python网络爬虫简介 从0开始学Python网络爬虫

# 爬虫原理和网页构造 网络连接原理: * 计算机----Request(请求头和消息体)--->服务器 * 计算机<---Respone(HTML文件)----服务器 爬虫原理: 1. 模拟计算机对服务器发起Request请求; 2. 接收服务器的Response内容并解析、提取所需的信息 ## 设计爬虫流程 多页面和跨页面爬虫流程。 ### 多页面爬虫流程 网页存在多页的情况,每页结构相同或相似。 1. 手动翻页观察各URL构成特点,构造成所有页面的URL存入列表; 2. 根据URL列表依次循环取出URL; 3. 定义爬虫函数; 4. 循环调用爬虫函数,存储数据; 5. 循环完毕,结束爬虫程序。 ### 跨页面爬虫流程 一个页面存在很多链接,每个链接进去对应一个详情页。 1. 定义爬虫函数爬取列表页所有专题的URL; 2. 将专题URL存入列表中(种子URL); 3. 定义爬取详细页数据函数; 4. 进入专题详细页面爬取详细页数据; 5. 存储数据,循环完毕,结束爬虫程序。 ## 网页构造 浏览器按F12,点击Elements可以看到网页的元素,包括`HTML`、`CSS`、`JavaScript` # 爬虫初步入门 创建虚拟环境 ```python >mkvirtualenv Crawler >workon Crawler >pip install requests >pip install lxml >pip install beautifulsoup4 ``` ## 爬虫三大库 ### Requests库 http://docs.python-requests.org/zh_CN/latest/user/quickstart.html 它的作用就是请求网站获取网页数据的。 ```python import requests res = requests.get(url='https://blog.starmeow.cn/blog/1/detail/') print(res) # <Response [200]>,如果为40x则请求失败 print(res.text) # 显示网页所有内容和F12结果一样 ``` **后端显示的浏览器Agent信息为:`python-requests/2.20.0`**,意思就是通过requests库访问的 有时爬虫需要加入请求头来伪装成浏览器,以便更好地抓取数据。 ![BLOG_20181104_221922_97](/media/blog/images/2018/11/BLOG_20181104_221922_97.png "博客图集BLOG_BLOG_20181104_221922_97.png") #### 请求头的使用方法 ```python import requests headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } res = requests.get(url='https://blog.starmeow.cn/blog/1/detail/', headers=headers) # get方法加入请求头 print(res) # <Response [200]>,如果为40x则请求失败 print(res.text) # 显示网页所有内容和F12结果一样 ``` 然后再查看网页的请求Agent信息就是`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36` Requests库不仅有`get()`方法,还有`post()`方法。`post()`方法用于提交表单来爬取需要登录才能获取数据的网站。 #### 错误和异常 requests.exceptions的官方文档 http://www.python-requests.org/en/master/_modules/requests/exceptions/#RequestException - Requests抛出一个`ConnectionError`异常,这个是网络问题(如DNS查询失败,拒绝连接等) - `Response.raise_for_status()`抛出一个`HTTPError`异常,为HTTP请求返回了不成功的状态码(如网页不存在,返回404错误) - Requests抛出一个`Timeout`异常,原因为请求超时 - Requests抛出一个`TooManyRedirects`异常,为请求超过了设置的最大重定向次数 所有Requests显示抛出的异常都继承`requests.exceptions.RequestException`,爬取过程如果遇到错误爬虫就会停止,可以通过`try`来避免异常。 ```python import requests headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } try: res = requests.get(url='https://blog.starmeow0.cn/blog/1/detail/', headers=headers) # get方法加入请求头 print(res) # <Response [200]>,如果为40x则请求失败 print(res.text) # 显示网页所有内容和F12结果一样 except (ConnectionRefusedError, requests.exceptions.ConnectionError): # 出现错误会显示一下内容 print('拒绝连接') ``` 当程序出现异常后,就不会直接报错,而是给一个提示,不会影响原来代码的运行。 ### BeautifulSoup库 可以轻松的解析Requests库请求的网页,并把网页源代码解析为Soup文档,以便过滤提取数据。 ```python import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } try: res = requests.get(url='https://blog.starmeow.cn/blog/1/detail/', headers=headers) # get方法加入请求头 soup = BeautifulSoup(res.text, 'html.parser') print(soup.prettify()) except requests.exceptions.ConnectionError: # 出现错误会显示一下内容 print('拒绝连接') ``` 看上去与`res.text`结果类似,但通过BeautifulSoup库解析得到的soup文档按照标准缩进格式的结构输出,为结构化的数据,为数据的过滤提取做好准备。 #### BeautifulSoup库的主要解析器 | 解析器 | 使用方法 | 优势 | 劣势 | | --- | --- | --- | --- | | Python标准库 | BeautifulSoup(markup, “html.parser”) | - Python的内置标准库- 执行速度适中- 文档容错能力强 | Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 | | lxml HTML 解析器 | BeautifulSoup(markup, “lxml”) | - 速度快- 文档容错能力强 | 需要安装C语言库 | | lxml XML 解析器 | BeautifulSoup(markup, \[“lxml”, “xml”\])BeautifulSoup(markup, “xml”) | - 速度快- 唯一支持XML的解析器 | 需要安装C语言库 | | html5lib | BeautifulSoup(markup, “html5lib”) | - 最好的容错性- 以浏览器的方式解析文档- 生成HTML5格式的文档 | 速度慢,不依赖外部扩展 | > BeautifulSoup库官方推荐使用lxml作为解析器,效率更高。 标准选择器 - find_all( name , attrs , recursive , text , **kwargs ) :可根据标签名、属性、内容查找文档 - find( name , attrs , recursive , text , **kwargs ) :find返回单个元素,find_all返回所有元素 - 其他 #### find_all()方法 ```python import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } try: res = requests.get(url='https://blog.starmeow.cn/blog/1/detail/', headers=headers) # get方法加入请求头 soup = BeautifulSoup(res.text, 'html.parser') # print(soup.prettify()) find_widget = soup.find_all('div', 'widget') # 查找class="widget"的所有div标签,最终结果以list方式展示 print(find_widget) find_thumbnail = soup.find_all('span', attrs={'class': 'thumbnail'}) # attrs参数定义一个字典参数来所有包含特殊属性的tag,包含thumbnail的class的所有span print(find_thumbnail) except requests.exceptions.ConnectionError: # 出现错误会显示一下内容 print('拒绝连接') ``` #### find()方法 `find()`方法与`find_all()`方法类似,只是`find_all()`方法返回时文档汇总复核条件的所有tag,是一个集合(`<class 'bs4.element.ResultSet'>`),`find()`方法返回的是一个Tag对象(`<class 'bs4.element.Tag'>`) #### select()方法 从浏览器选择功能**Copy selector**,得到的结果用户信息的获取。 ```python import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } try: res = requests.get(url='https://blog.starmeow.cn/blog/1/detail/', headers=headers) # get方法加入请求头 soup = BeautifulSoup(res.text, 'html.parser') title = soup.select("body > section > div > div > header > h1 > a") # 右键Elements位置---Copy---Copy selector print(title) # [<a title="创建项目初始化">创建项目初始化</a>] hot_titles = soup.select("body > section > aside > div.widget.widget_hot > ul > li > a > span.text") # li:nth-child(1)运行会报错,需改为li print(hot_titles) """ 复制结果: body > section > aside > div.widget.widget_hot > ul > li:nth-child(1) > a > span.text 结果会报错,需要进行修改 body > section > aside > div.widget.widget_hot > ul > li:nth-of-type(1) > a > span.text 得到第一个数据 body > section > aside > div.widget.widget_hot > ul > li > a > span.text 得到所有数据 """ for title in hot_titles: print(title, title.get_text(), title.get_text() == title.text) """ <span class="text">【Flask微电影】05.搭建前台页面-会员登录注册和会员中心</span> 【Flask微电影】05.搭建前台页面-会员登录注册和会员中心 True <span class="text">【Flask微电影】10.搭建后台页面-会员管理、评论管理</span> 【Flask微电影】10.搭建后台页面-会员管理、评论管理 True <span class="text">【Flask微电影】11.搭建后台页面-收藏管理、日志管理</span> 【Flask微电影】11.搭建后台页面-收藏管理、日志管理 True <span class="text">【Flask微电影】07.搭建后台页面-后台登陆、后台主页页面</span> 【Flask微电影】07.搭建后台页面-后台登陆、后台主页页面 True <span class="text">【Flask微电影】01.环境搭建项目目录分析</span> 【Flask微电影】01.环境搭建项目目录分析 True """ except requests.exceptions.ConnectionError: # 出现错误会显示一下内容 print('拒绝连接') ``` 使用`title.get_text()`或者`title.text`可以得到标签的文字信息 ### Lxml库 基于libxml2虚怀若谷人XML解析库的Python封装,使用C语言编写,解析速度比BeautifulSoup快。 ## 实例1:爬取成都地区短租房信息 ### 思路分析 访问:https://cd.xiaozhu.com/ 查看网页的信息 分页url地址: - https://cd.xiaozhu.com/ 或者 https://cd.xiaozhu.com/search-duanzufang-p1-0/ - https://cd.xiaozhu.com/search-duanzufang-p2-0/ - https://cd.xiaozhu.com/search-duanzufang-p3-0/ - https://cd.xiaozhu.com/search-duanzufang-p4-0/ 根据url构造,只需要改变p后面的数字,可以构造出所有页面网址。然后根据这些url进入到详情页,获取标题、地址、价格、房东等信息。 ### 爬虫代码和注释 ```python import requests from bs4 import BeautifulSoup import time headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } # 用于伪装浏览器,便于爬虫的稳定性 def get_all_links(url): """ 得到每个分页的房子url列表 :param url: 房源列表的分页url :return: 得到每一页的所有房源列表 """ html_data = requests.get(url=url, headers=headers) soup = BeautifulSoup(html_data.text, 'lxml') # print(soup) links = soup.select('#page_list > ul > li > a') res = list() for link in links: # print(link.get('href')) href = link.get('href') # 获取标签href属性信息,得到进入详情页的URL res.append(href) time.sleep(2) return res def get_info(url): """ 每一个房源信息获取 :param url: 房源url :return: 房源字典信息 """ html_data = requests.get(url, headers=headers) soup = BeautifulSoup(html_data.text, 'lxml') titles = soup.select('div.wrap.clearfix.con_bg > div.con_l > div.pho_info > h4 > em') # 复制的结果是:body > div.wrap.clearfix.con_bg > div.con_l > div.pho_info > h4 > em,无法获取,需要去掉body title = '' if titles: title = titles[0].get_text().strip() # 如果得到的结果不为空,取第一个的值 addresses = soup.select('div.wrap.clearfix.con_bg > div.con_l > div.pho_info > p > span') # print(addresses) address = '' if addresses: address = addresses[0].get_text().strip() prices = soup.select('#pricePart > div.day_l > span') price = '' if prices: price = prices[0].get_text() images = soup.select('#curBigImage') image = '' if images: image = images[0].get('src') names = soup.select('#floatRightBox > div.js_box.clearfix > div.w_240 > h6 > a') name = '' if names: name = names[0].get_text() sexs = soup.select('#floatRightBox > div.js_box.clearfix > div.member_pic > div') sex = '' if sexs: sex = sexs[0].get('class') # print(sex) # ['member_ico1'] if "member_ico1" in sex: # 根据class来判断房东的性别,男房东的class="member_ico" sex = '女' else: sex = '男' data = { 'title': title, 'address': address, 'price': price, 'image': image, 'name': name, 'sex': sex } # print(data) return data if __name__ == '__main__': # fangzi_list = get_all_links('https://cd.xiaozhu.com/') # fangzi_info = get_info('https://cd.xiaozhu.com/fangzi/2148542359.html') page_urls = ['https://cd.xiaozhu.com/search-duanzufang-p{}-0/'.format(num) for num in range(1, 4)] # 准备3页数据 for page_url in page_urls: fangzi_list = get_all_links(page_url) # 得到每一页的所有房源列表 time.sleep(2) # 暂停2秒,防止请求过快导致被服务器拒绝 for fangzi in fangzi_list: print(get_info(fangzi)) # 遍历房源列表,得到每个房源信息 # 运行结果 # {'title': '宽窄巷子人民公园双地铁2号4号景区房熊猫风', 'address': '四川省成都市青羊区通惠门69号长富新城2栋24楼', 'price': '188', 'image': 'https://image.xiaozhustatic1.com/00,800,533/14,0,39,25214,1800,1200,71ccb694.jpg', 'name': '周周小家', 'sex': '男'} # {'title': '地铁口春熙路太古里,熊猫基地大二居3床住5人', 'address': '四川省成都市成华区前锋路6号阳光欣园', 'price': '328', 'image': 'https://image.xiaozhustatic1.com/00,800,533/13,0,61,31508,1798,1200,d0c0cd6e.jpg', 'name': '幺妹家', 'sex': '女'} ``` ## 实例2:爬取库存TOP500数据 ### 思路分析 TOP500第一页链接:http://www.kugou.com/yy/rank/home/1-8888.html?from=rank 网页版不能手动翻页,但观察第一页的URL,将1改为2:http://www.kugou.com/yy/rank/home/2-8888.html?from=rank 正好是第二页的列表,浏览发现,每一页显示22条数据,所以500条,23页能显示完 需要爬取的信息有排名、歌手、歌曲、歌曲时长 ### 爬虫代码和注释 ```python import requests from bs4 import BeautifulSoup import time headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" } # 用于伪装浏览器,便于爬虫的稳定性 def get_info(url): html_data = requests.get(url=url, headers=headers) soup = BeautifulSoup(html_data.text, 'lxml') ranks = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_num') # 前3个数字加粗,select不能包含strong标签 # print(ranks[0].get_text().strip(), ranks[4].get_text().strip()) # 获取歌曲的信息 songs = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > a') length_of_times = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_tips_r > span') for rank, song, length_of_time in zip(ranks, songs, length_of_times): data = { 'rank': rank.get_text().strip(), # 排名 'singer': song.get_text().split('-')[0].strip(), # 歌手 'name': song.get_text().split('-')[1].strip(), # 歌曲名 'url': song.get('href'), # 歌曲链接 'length_of_time': length_of_time.get_text().strip() # 时长 } print(data) return data if __name__ == '__main__': get_info('http://www.kugou.com/yy/rank/home/1-8888.html?from=rank') top500_list = ['http://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'.format(num) for num in range(1, 3)] # range(1, 24)显示所有页 for url in top500_list: get_info(url) time.sleep(2) # 运行结果 # {'rank': '1', 'singer': '展展与罗罗', 'name': '沙漠骆驼', 'url': 'http://www.kugou.com/song/g3d726a.html', 'length_of_time': '5:38'} # {'rank': '2', 'singer': '黑龙', 'name': '38度6', 'url': 'http://www.kugou.com/song/ootaieb.html', 'length_of_time': '3:11'} # {'rank': '3', 'singer': '张紫豪', 'name': '可不可以', 'url': 'http://www.kugou.com/song/mkt6v7f.html', 'length_of_time': '4:00'} # {'rank': '4', 'singer': 'G.E.M.邓紫棋', 'name': '光年之外', 'url': 'http://www.kugou.com/song/eoo8m01.html', 'length_of_time': '3:55'} # {'rank': '5', 'singer': '半阳', 'name': '流浪', 'url': 'http://www.kugou.com/song/ndiin31.html', 'length_of_time': '3:39'} ``` ### zip()函数使用 #### 描述 `zip()` 函数用于将可迭代对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象。 如果各个可迭代对象的元素个数不一致,则返回的对象长度与最短的可迭代对象相同。 利用 `*` 号操作符,与`zip`相反,进行解压。 #### 语法 `zip()` 函数语法: `zip(iterable1,iterable2, ...)` 参数说明: - `iterable` -- 一个或多个可迭代对象(字符串、列表、元祖、字典) 返回的是一个对象,如果想要得到列表,可以用 `list()` 函数进行转换。 #### 实例 ```python >>> a = [1,2,3] #此处可迭代对象为列表 >>> b = [4,5,6] >>> c = [4,5,6,7,8] >>> zipped = zip(a,b) >>> zipped # <zip object at 0x02B01B48> #返回的是一个对象 >>> list(zipped) # [(1, 4), (2, 5), (3, 6)] #使用list()函数转换为列表 >>> list(zip(a,c)) # [(1, 4), (2, 5), (3, 6)] >>> zipped = zip(a,b) >>> list(zip(*zipped)) #解压也使用list进行转换 # [(1, 2, 3), (4, 5, 6)] ``` ```python #v1,v2,v3可是是任何可迭代对象,如:字符串、列表、元祖、字典 v1 = {1:11,2:22} #此处可迭代对象为字典 v2 = {3:33,4:44} v3 = {5:55,6:66} v = zip(v1,v2,v3) #压缩 print(list(v)) w = zip(*zip(v1,v2,v3)) #解压 print(list(w)) # [(1, 3, 5), (2, 4, 6)] # [(1, 2), (3, 4), (5, 6)] ``` 搭配for循环支持并行迭代 ```python list1 = [2,3,4] list2 = [4,5,6] for x,y in zip(list1,list2): print(x,y,'--',x*y) # 2 4 -- 8 # 3 5 -- 15 # 4 6 -- 24 ```

很赞哦! (0)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行552天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.9
  • 文章统计:219 篇
  • 文章评论:37 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 微信公众号:扫描二维码,关注我们
返回
顶部
标题 换行 登录
网站