这边有一个用来测试的网站点击跳转

简单的网页爬虫

  • requests的使用
    • 使用requests获取网页的源代码
    • requests与正则结合
  • 多线爬虫
    • 多进程库
    • 开发多线程爬虫
  • 爬虫算法的开发
    • 深度优先搜索
    • 广度优先搜索
    • 算法的选择
  • 小测试

requests的使用

requestsPython的一个第三方HTTPHypertext Transfer Protocol,超文本传输协议)库,它比Python自带的网络库urllib更加简单、方便和人性化。使用requests可以让Python实现访问网页并获取源代码的功能。

使用requests获取网页的源代码,最简单的情况下只需要两行代码:

#使用requests获取源代码
import requests
source = requests.get('https://www.baidu.com').content.deocde()

使用requests获取网页的源代码

使用浏览器来访问网页,看起来只需要输入网址就可以。但其实网页有很多种打开方式,最常见的是GET方式和POST方式。

在浏览器里面可以直接通过输入网址访问的页面,就是使用了GET方式。

还有一些页面,只能通过从另一个页面单击某个链接或者某个按钮以后跳过来,不能直接通过在浏览器输入网址访问,这种网页就是使用了POST方式。


GET方式

  • 对于使用GET方式的网页,再Python中可以使用requestsget方法获取网页的原码

    url = 'https://www.baidu.com'
    html = requests.get(url)
    html_text = html.text()
    html_bytes = html.content
    html_str = html_bytes.decode()
    
  • html是全部的网页数据

  • content是将数据转换成二进制数据,一般用于读取图片数据

  • text是将数据转换成网页自己编码的字符串,一般用于读取文字数据

  • decode将数据从二进制转换成字符串


POST方式

  • 网页的访问方式除了GET方式以外,还有POST方式。有一些网页,使用GETPOST方式访问同样的网址,得到的结果是不一样的。还有另外一些网页,只能使用POST方式访问,如果使用GET方式访问,网站会直接返回错误信息。

    import requestsurl = 'http://exercise.kingname.info/exercise_requests_post'
    data = {'name': 'value1','password': 'value2'
    }
    html = requests.post(url, data=data).content.decode()

  • 其中,data这个字典的内容和项数需要根据实际情况修改,KeyValue在不同的网站是不一样的。而做爬虫,构造这个字典是任务之一。

  • 还有一些网址,提交的内容需要是JSON格式的,因此post()方法的参数需要进行一些修改:

    import requestsurl = 'http://exercise.kingname.info/exercise_requests_post'
    data = {'name': 'value1','password': 'value2'
    }
    json = {'name': 'value1','password': 'value2'
    }
    html_data = requests.post(url, data=data).content.decode()
    html_json = requests.post(url, json=data).content.decode()
    

跳转顶部


requests与正则结合

在上面使用requests我们可以拿到下面的数据


那我们该如何拿到标题和正文?

import requests
import reurl = 'http://exercise.kingname.info/exercise_requests_get.html'page = requests.get(url).content.decode()title = re.search('title>(.*?)<', page, re.S)[1]
print(title)
content = re.findall('p>(.*?)<', page, re.S)
# 将文本拼接再一起
content_str = ''.join(content)
print(content_str)


search和finall的区别就是:前者返回第一个符合条件的数据,后者返回的是一个数组
跳转顶部


多线爬虫

虽然我们初步掌握了requests与正则表达式以后,可以爬取一些简单的网址,但是此时的爬虫只有一个进程、一个线程,因此称为单线程爬虫。单线程爬虫每次只访问一个页面,不能充分利用计算机的网络带宽。一个页面最多也就几百KB,所以爬虫在爬取一个页面的时候,多出来的网速和从发起请求到得到源代码中间的时间都被浪费了。

如果可以让爬虫同时访问10个页面,就相当于爬取速度提高了10倍。为了达到这个目的,就需要使用多线程技术了。

微观上的单线程,在宏观上就像同时在做几件事。这种机制在I / OInput / Output,输入 / 输出)密集型的操作上影响不大,但是在CPU计算密集型的操作上面,由于只能使用CPU的一个核,就会对性能产生非常大的影响。所以涉及计算密集型的程序,就需要使用多进程,Python的多进程不受GIL的影响

爬虫属于I / O密集型的程序,所以使用多线程可以大大提高爬取效率。

多进程库

multiprocessing本身是Python的多进程库,用来处理与多进程相关的操作。但是由于进程与进程之间不能直接共享内存和堆栈资源,而且启动新的进程开销也比线程大得多,因此使用多线程来爬取比使用多进程有更多的优势。multiprocessing下面有一个dummy模块,它可以让Python的线程使用multiprocessing的各种方法。

dummy下面有一个Pool类,它用来实现线程池。这个线程池有一个map()方法,可以让线程池里面的所有线程都“同时”执行一个函数。


例如,计算0到10000每个数的平方

  • 一般我们都是按照下面的代码来写
startTime = datetime.datetime.now()for num in range(10000):print(num * num)endTime = datetime.datetime.now()
print(f'执行代码的时间是{endTime - startTime}')


使用多线程的方式

startTime = datetime.datetime.now()def calc_power2(num):return num * numpool = Pool(3)
origin_num = [x for x in range(10000)]
result = pool.map(calc_power2, origin_num)
print(f'计算0-9的平方分别为:{result}')
endTime = datetime.datetime.now()
print(f'执行代码的时间是{endTime - startTime}')


在上面的代码中,先定义了一个函数用来计算平方,然后初始化了一个有3个线程的线程池。这3个线程负责计算10000个数字的平方,谁先计算完手上的这个数,谁就先取下一个数继续计算,直到把所有的数字都计算完成为止。

在这个例子中,线程池的map()方法接收两个参数,第1个参数是函数名,第2个参数是一个列表。注意:第1个参数仅仅是函数的名字,是不能带括号的。第2个参数是一个可迭代的对象,这个可迭代对象里面的每一个元素都会被函数clac_power2()接收来作为参数。除了列表以外,元组、集合或者字典都可以作为map()的第2个参数。

跳转顶部


开发多线程爬虫

由于爬虫是I / O密集型的操作,特别是在请求网页源代码的时候,如果使用单线程来开发,会浪费大量的时间来等待网页返回,所以把多线程技术应用到爬虫中,可以大大提高爬虫的运行效率。

系啊面写一段代码来比较

import requests
import time
from multiprocessing.dummy import Pooldef query(url):requests.get(url)start = time.time()
for i in range(100):query('https://baidu.com')
end = time.time()
print(f'单线程循环访问100次百度首页,耗时:{end - start}')start = time.time()
url_list = []
for i in range(100):url_list.append('https://baidu.com')
pool = Pool(5)
pool.map(query, url_list)
end = time.time()
print(f'5线程访问100次百度首页,耗时:{end - start}')


发现使用多线程比单线程的速度快乐很多,但是是不是线程越多越好呢?并不是,因为我们可以发现5个线程运行的时间其实是比单个线程运行的五分之一是要多的,也就是线程也是需要消耗资源的。这也从侧面反映了Python的多线程再微观上还是串行的,所以如果线程池设置的过大线程的切换而导致的开销可能会抵消多线程带来的性能提升
跳转顶部


爬虫算法的开发

在角色扮演类游戏中,玩家需要在游戏里领取任务。有的人喜欢一次只领取一个任务,把这个任务做完,再去领下一个任务,这就叫作深度优先搜索。

还有一些人喜欢先把能够领取的所有任务一次性领取完,然后去慢慢完成,最后再一次性把任务奖励都领取了,这就叫作广度优先搜索。


深度优先搜索

假设下图是某在线教育网站的课程分类,需要爬取上面的课程信息。从首页开始,课程有几个大的分类,比如根据语言分为PythonNode.jsGolang。每个大分类下面又有很多的课程,比如Python下面有爬虫、Django和机器学习。每个课程又分为很多的课时。


在深度搜素优先的情况下,爬取的路线如图所示(序号从小到大)

也就是爬虫会先将一个课程全部爬取完成后,才继续爬取下一个内容

广度优先搜索

在广度优先搜索的情况下,爬取路线的顺序如下所示

也就是会先爬取第一个大类,第一个打雷全部爬取完成之后爬取第二个打类……

算法的选择

在爬虫开发的过程中,应该选择深度优先还是广度优先呢?这就需要根据被爬取的数据来进行选择了。

例如要爬取某网站全国所有的餐馆信息和每个餐馆的订单信息。假设使用深度优先算法,那么先从某个链接爬到了餐馆A,再立刻去爬餐馆A的订单信息。由于全国有十几万家餐馆,全部爬完可能需要12小时。这样导致的问题就是,餐馆A的订单量可能是早上8点爬到的,而餐馆B是晚上8点爬到的。它们的订单量差了12小时。

而对于热门餐馆来说,12小时就有可能带来几百万的收入差距。这样在做数据分析时,12小时的时间差就会导致难以对比A和B两个餐馆的销售业绩。

相对于订单量来说,餐馆的数量变化要小得多。所以如果采用广度优先搜索,先在半夜0点到第二天中午12点把所有的餐馆都爬取一遍,第二天下午14点到20点再集中爬取每个餐馆的订单量。这样做,只用了6个小时就完成了订单爬取任务,缩小了由时间差异致的订单量差异。同时由于店铺隔几天抓一次影响也不大,所以请求量也减小了,使爬虫更难被网站发现。

所以在爬取数据量大的情况下建议使用广度优先,数据量较小的情况下建议使用深度优先

跳转顶部


小测试

需求

从http: //www.kanunu8.com / book3/6879爬取《动物农场》所有章节的网址,再通过一个多线程爬虫将每一章的内容爬取下来。在本地创建一个“动物农场”文件夹,并将小说中的每一章分别保存到这个文件夹中。每一章保存为一个文件。

需要读取每一章节的内容就需要拿到每一张的地址,我们可以发现每一章节的地址就是下图中红色圈住的地方不一致而已不一样而已,我们只需要拿到最后的内容即可

url = 'http://www.kanunu8.com/book3/6879/'html = requests.get(url).content.decode('gbk')
title_url_list = re.findall('a href="(\d+.html)">', html, re.S)


接下来我们需要拼接处每个章节的地址

title_url = []
for i in title_url_list:title_url.append(url + i)


随便点击一个地址

将上面的代码写到一个函数里面

import requests
from multiprocessing.dummy import Pool
import lxml.html
import reurl = 'http://www.kanunu8.com/book3/6879/''''
输入网页网址,输出page_text
'''def get_source(url):return requests.get(url).content.decode('gbk')'''
构建一个方法获取每一个章节的url,并且返回一个list
'''def get_title_url(html):title_url_list = re.findall('a href="(\d+.html)">', html, re.S)# 拼接出每个章节地址title_url = []for i in title_url_list:title_url.append(url + i)return title_urlif __name__ == '__main__':html = get_source(url)title_url = get_title_url(html)

现在构建一个处理单个章节的函数

def get_content(html):chapter_name = re.search('size="4">(.*?)<', html, re.S).group(1)text_block = re.search('<p>(.*?)</p>', html, re.S).group(1)text_block = text_block.replace('<br />', '')return chapter_name, text_block

在写一个保存函数

def save(chapter, article):os.makedirs('动物农场', exist_ok=True)  # 如果没有"动物农场文件夹,就创建一个,如果有,则什么都不做"with open(os.path.join('动物农场', chapter + '.txt'), 'w', encoding='utf-8') as f:f.write(article)

编写一个函数来调用上述的几个函数

def query_content(url):content_html = get_source(url)chapter_name, text_block = get_content(content_html)save(chapter_name, text_block)

运行结果如下所示

完整代码如下

import requests
from multiprocessing.dummy import Pool
import lxml.html
import os
import reurl = 'http://www.kanunu8.com/book3/6879/''''
输入网页网址,输出page_text
'''def get_source(url):return requests.get(url).content.decode('gbk')'''
构建一个方法获取每一个章节的url,并且返回一个list
'''def get_title_url(html):title_url_list = re.findall('a href="(\d+.html)">', html, re.S)# 拼接出每个章节地址title_url = []for i in title_url_list:title_url.append(url + i)return title_url'''
处理每一个章节
输入章节html
输出文章和标题
'''def get_content(html):chapter_name = re.search('size="4">(.*?)<', html, re.S).group(1)text_block = re.search('<p>(.*?)</p>', html, re.S).group(1)text_block = text_block.replace('<br />', '')return chapter_name, text_block'''
保存函数
'''def save(chapter, article):os.makedirs('动物农场', exist_ok=True)  # 如果没有"动物农场文件夹,就创建一个,如果有,则什么都不做"with open(os.path.join('动物农场', chapter + '.txt'), 'w', encoding='utf-8') as f:f.write(article)'''
获取每一个章节的url,变成html传输个体get_content方法
并且调用save方法保存
'''def query_content(url):content_html = get_source(url)chapter_name, text_block = get_content(content_html)save(chapter_name, text_block)if __name__ == '__main__':html = get_source(url)title_url = get_title_url(html)pool = Pool(4)pool.map(query_content, title_url)

跳转顶部


【Python 爬虫】简单的网页爬虫相关推荐

  1. Python之简单的网页爬虫开发

    Python之简单的网页爬虫开发 文章目录 Python之简单的网页爬虫开发 下面简单介绍一下request: 简单介绍一下什么是第三方库: 结合requests与正则表达式 多线程爬虫 多进程库(m ...

  2. 利用 Python 实现简单的主题爬虫

    利用 Python 实现简单的主题爬虫   利用 Python 实现简单的主题爬虫,主要是通过对指定的 主题 和 网站 进行深度爬取,获取对应网页的标题和 url ,仅供学习参考. 爬取结果: 实验源 ...

  3. python制作查询网页_Python制作简单的网页爬虫

    1.准备工作: 工欲善其事必先利其器,因此我们有必要在进行Coding前先配置一个适合我们自己的开发环境,我搭建的开发环境是: 操作系统:Ubuntu 14.04 LTS Python版本:2.7.6 ...

  4. python制作简单网页_Python制作简单的网页爬虫

    1.准备工作: 工欲善其事必先利其器,因此我们有必要在进行Coding前先配置一个适合我们自己的开发环境,我搭建的开发环境是: 操作系统:Ubuntu 14.04 LTS Python版本:2.7.6 ...

  5. 实战|Python轻松实现动态网页爬虫(附详细源码)

    用浅显易懂的语言分享爬虫.数据分析及可视化等干货,希望人人都能学到新知识. 项目背景 事情是这样的,前几天我公众号写了篇爬虫入门的实战文章,叫做<实战|手把手教你用Python爬虫(附详细源码) ...

  6. beautifulsoup解析动态页面div未展开_实战|Python轻松实现动态网页爬虫(附详细源码)...

    用浅显易懂的语言分享爬虫.数据分析及可视化等干货,希望人人都能学到新知识.项目背景事情是这样的,前几天我公众号写了篇爬虫入门的实战文章,叫做<实战|手把手教你用Python爬虫(附详细源码)&g ...

  7. 3.简单的网页爬虫开发

    目录 一.爬虫开发中的法律与道德问题 1.数据采集的法律问题 (1)妨害个人信息安全 (2)涉及国家安全信息 (3)妨害网站正常运行 (4)侵害他人利益 (5)内幕交易 2.道德协议 (1)Robot ...

  8. python最简单的小爬虫

    网络爬虫: 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模 ...

  9. python爬虫数据挖掘_Python网页爬虫文本处理科学计算机器学习数据挖掘兵器谱...

    转载自"我爱自然语言处理":http://www.52nlp.cn,已获得授权.更多内容可见公众号:"牛衣古柳"(ID:Deserts-X). 周末时看到这篇不 ...

最新文章

  1. 解决掉这些痛点和难点,让知识图谱不再是“噱头”
  2. FireFox不支持cursor:hand
  3. PAT:1034 Head of a Gang (30分)
  4. 【Linux】一步一步学Linux——ypdomainname命令(173)
  5. haproxy详细介绍
  6. 中国.NET:东莞+长沙.NET俱乐部现场花絮及合肥、苏州、上海等地活动预
  7. java学习(56):接口之间的继承续
  8. 计算机二级考vb试题,2017计算机二级考试VB练习题及答案
  9. PAT1124:Raffle for Weibo Followers
  10. ggplot2 | 坐标标度函数、坐标系统函数
  11. Flutter入门进阶之旅(七)GestureDetector
  12. 多语言可视化编辑webControl 2.0版
  13. insert into 多条数据_最全总结 | 聊聊 Python 数据处理全家桶(MongoDB 篇)
  14. 计算机里一共录入多少个汉字,计算机操作员之汉字输入方法
  15. matlab空间面板门槛,重磅!这可能是最全的面板门槛回归汇总了
  16. 计算机无法通过无线上网,笔记本电脑突然无法使用无线网卡的多种解决方法
  17. WORD目录中一级标题和二级标题小圆点不同的原因
  18. Unity3D 2D游戏摄像机的跟随实现
  19. Linux下挂载NTFS分区
  20. 音乐制作编曲工具FL Studio 20高清视频教程

热门文章

  1. Amazon Aurora 并行查询——加速分析处理的利器
  2. Virtual box中win7分辨率问题解决
  3. 【GBT28181开发:SIP协议实践】之注册流程
  4. 配置tensorflow-gpu,大过年的头秃啊
  5. 计算机课代表自荐信,课代表自荐信
  6. 4.1 用户和组管理
  7. IDEA文件UTF-8格式控制台输出中文乱码
  8. 机器学习:非监督学习
  9. 虚拟+现实:半实物仿真测试和全数字仿真测试有效保证嵌入式系统的健壮与可靠
  10. 超平面、Wx=b或Wx+b=0的几何意义