Python爬虫 您所在的位置:网站首页 怎么抓网页源码文件夹 Python爬虫

Python爬虫

2024-07-09 11:16| 来源: 网络整理| 查看: 265

  之前没学过Python,最近因一些个人需求,需要写个小爬虫,于是就搜罗了一批资料,看了一些别人写的代码,现在记录一下学习时爬过的坑。

  如果您是从没有接触过Python的新手,又想迅速用Python写出一个爬虫,那么这篇文章比较适合你。

  首先,我通过:

  https://mp.weixin.qq.com/s/ET9HP2n3905PxBy4ZLmZNw

找到了一份参考资料,它实现的功能是:爬取当当网Top 500本五星好评书籍

  源代码可以在Github上找到:

  https://github.com/wistbean/learn_python3_spider/blob/master/dangdang_top_500.py

然而,当我运行这段代码时,发现CPU几乎满负荷运行了,却根本没有输出。

现在我们来分析一下其源代码,并将之修复。

  先给出有问题的源码:

 

1 import requests 2 import re 3 import json 4 5 6 def request_dandan(url): 7 try: 8 response = requests.get(url) 9 if response.status_code == 200: 10 return response.text 11 except requests.RequestException: 12 return None 13 14 15 def parse_result(html): 16 pattern = re.compile( 17 '.*?list_num.*?(\d+)..*?.*?class="star">.*?class="tuijian">(.*?).*?class="publisher_info">.*?target="_blank">(.*?).*?class="biaosheng">.*?(.*?).*?

¥(.*?).*?', 18 re.S) 19 items = re.findall(pattern, html) 20 21 for item in items: 22 yield { 23 'range': item[0], 24 'iamge': item[1], 25 'title': item[2], 26 'recommend': item[3], 27 'author': item[4], 28 'times': item[5], 29 'price': item[6] 30 } 31 32 33 def write_item_to_file(item): 34 print('开始写入数据 ====> ' + str(item)) 35 with open('book.txt', 'a', encoding='UTF-8') as f: 36 f.write(json.dumps(item, ensure_ascii=False) + '\n') 37 38 39 def main(page): 40 url = 'http://bang.dangdang.com/books/fivestars/01.00.00.00.00.00-recent30-0-0-1-' + str(page) 41 html = request_dandan(url) 42 items = parse_result(html) # 解析过滤我们想要的信息 43 for item in items: 44 write_item_to_file(item) 45 46 47 if __name__ == "__main__": 48 for i in range(1, 26): 49 main(i)

 

  是不是有点乱?别急,我们来一步步分析。(如果您不想看大段的分析,可以直接跳到最后,在那里我会给出修改后的,带有完整注释的代码)

  首先,Python程序中的代码是一行行顺序执行的,前面都是函数定义,因此直接先运行第47-49行的代码:

if __name__ == "__main__": for i in range(1, 26): main(i)

 

  看样子这里是在调用main函数(定义在第39行),那么__name__是什么呢?

  __name__是系统内置变量,当直接运行包含main函数的程序时,__name__的值为"__main__",因此main函数会被执行,而当包含main函数程序作为module被import时,__name__的值为对应的module名字,此时main函数不会被执行。

  为了加深理解,可以阅读这篇文章,讲得非常清楚:

  https://www.cnblogs.com/keguo/p/9760361.html

我们的程序里是直接运行包含main函数的程序的,因此__name__的值就是__main__。   

 

还有个小细节需要注意一下:

  像Lua这种语言,函数在结束之前会有end作为函数结束标记,包括if,for这种语句,都会有相应的end标记。但Python中是没有的,Python中是用对应的缩进来表示各个作用域的,我们把第47-49行的代码稍微改一下来进一步说明:

  新建个Python文件,直接输入:

if __name__ == "__main__": for i in range(1,5): print("内层") print("外层")

  此时for语句比if语句缩进更多,因此位于if的作用域内,同理,print("内层")语句位于for语句的作用域内,因此会打印5次,print("外层")已经不在for语句的作用域内,而在if语句的作用域内,因此只打印1次,运行结果如下:

    

  那么47-49行做的就是循环调用25次main函数(range左闭右开),为什么是25次呢?因为要爬取的当当网好评榜一页有20本图书数据,要爬500本我们需要发送25次数据请求。

  我们看一下main函数(39-44行)做了什么:

  首先进行了url的拼接,每次调用时传入不同的page,分别对应第1-25页数据。随后调用request_dandan发送数据请求,看一下request_dandan(第6-12行)做了什么:

  这里调用了requests模块向服务器发送get请求,因此要在程序开头导入requests模块(第1行),get请求去指定的url获取网页数据,随后对响应码作了判断,200代表获取成功,成功就返回获取的响应数据。要注意的一点是,这里get请求是同步请求,意思是发送请求后程序会阻塞在原地,直到收到服务器的响应后继续执行下一行代码。

  接下来main函数要调用parse_result(第15到30行)对获取到的html文本进行解析,提取其中与图书有关的信息,在分析这段代码之前,我们需要先了解下返回的html文件的格式:

我们可以在chrome浏览器中的开发者工具里,查看对应请求网页响应的html格式,以我的为例: 

 以第一本书“有话说出来”为例,用Command+F(Mac下)快速翻找一下与要爬取的图书有关的信息:

   每一本书的信息格式是这样的:

1. span"/spanspan有话说出来!(彻底颠覆社会人脉的固有方式,社交电池帮你搞定社交。社交恐惧症患者必须拥有的一本实用社交指南,初入大学和职场的必备“攻略”,拿起这本书,你也是“魏璎珞”)纤阅出品/spanspan"/span 有话说出来!(彻底颠覆社会人脉的固有方式,社交电池帮你搞定社... 17757条评论100%推荐 【美】帕特里克·金 著,张捷/李旭阳 译 2018-08-01;天津人民出版社 五星评分:16273次

¥30.40 ¥42.00(7.2折)

加入购物车 收藏

  是不是很乱?不要急,我们慢慢来分析,首先我们要明确自己要提取图书的哪部分信息,我们这里决定爬取它的:

排名,书名,图片地址,作者,推荐指数,五星评分次数和价格。

  那么对这么大段的html文本,怎么提取每本书的相关信息呢?答案自然是通过正则表达式,在parse_result函数中,先构建了用来匹配的正则表达式(第16行),随后对传入的html文件执行匹配,获取匹配结果(第19行),注意,这一步需要re模块的支持(在第1行导入re模块),re.compile是对匹配符的封装,直接用re.match(匹配符,要匹配的原文本)可以达到相同的效果, 当然,这里没有用re.match来执行匹配,而是用了re.findall,这是因为后者可以适用于多行文本的匹配。另外,re.compile后面的第2个参数,re.S是用来应对换行的,.匹配的单个字符不包括\n和\r,当遇到换行时,我们需要用到re.S。

  上面的这段表述可能不大清楚,具体re模块的正则匹配用法请自行百度,配合自己动手实验才能真正明白,这里只能描述个大概,另外,我们这里不会从头开始讲解正则表达式的种种细节,而是仅对代码中用到的正则表达式进行分析,要了解更多正则表达式相关的消息,就需要您自行百度了,毕竟对一个程序员来说,自学能力还是很重要的。

  好,我们来看下代码用到的正则表达式:

  一段段来分析,首先是:

.*?list_num.*?(\d+).

  .代表匹配除了\n和\r之外的任意字符,*代表匹配0次或多次,?跟在限制符(这里是*)后面是代表使用非贪婪模式匹配,因为默认的正则匹配是贪婪匹配,比如下面这段代码:

import re content = 'abcabc' res = re.match('a.*c',content) print(res.group())

  此时匹配时会匹配尽可能长的字符串,因此会输出abcabc,而若把a.*c改为a.*c?,此时是非贪婪匹配,会匹配尽可能少的字符串,因此会输出abc。

  然后是\d,代表匹配一个数字,+代表匹配1个或多个。因此上面的表达式匹配的就是html文本中下图所示的部分:

  

  注意,\d+被括号括起来了,代表将匹配的这部分内容(即图中的1这个数字)捕获并作为1个元素存放到了一个数组中,所以现在匹配结果对应的数组中(即item)第一个元素是1,也就是排名。

   随后是

.*?


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有