php 循环抓取网页内容(PythonFor和While循环不确定页数的网页需要学习的地方)
优采云 发布时间: 2021-11-22 06:27php 循环抓取网页内容(PythonFor和While循环不确定页数的网页需要学习的地方)
本文转载自以下网站:Python For和While循环爬取页数不确定的网页
学习的地方
有两种方法。
第一种方法使用带有 break 语句的 For 循环。最后一页的页数设置了更大的参数,足以循环遍历所有页。爬行完成后,break会跳出循环,结束爬行。
第二种方法使用While循环,可以和break语句结合使用,也可以设置初始循环判断条件为True,从头开始循环爬行,直到爬到最后一页,再将判断条件改为False跳出循环并结束爬行。
Requests 和 Scrapy 分别使用 For 循环和 While 循环来抓取页数不确定的网页。
摘要:Requests 和 Scrapy 分别使用 For 循环和 While 循环来抓取页面数量不确定的网页。
我们通常遇到的网站页码的显示形式有以下几种:
一是将所有页码可视化显示,比如之前爬取的宽和东方财富。
文章见:
二是不直观显示网页总数,只能在后台查看。比如之前爬过的虎嗅网络,见文章:
第三个是我今天要讲的。不知道有多少页,比如豌豆荚:
对于前两种形式的网页,爬取的方法很简单,只用For循环从第一页爬到最后一页,第三种形式不适用,因为最后一页的编号不知道,所以循环到哪一页无法判断。
如何解决?有两种方法。
第一种方法使用带有 break 语句的 For 循环。最后一页的页数设置了更大的参数,足以循环遍历所有页。爬行完成后,break会跳出循环,结束爬行。
第二种方法使用While循环,可以和break语句结合使用,也可以设置初始循环判断条件为True,从头开始循环爬行,直到爬到最后一页,再将判断条件改为False跳出循环并结束爬行。
实际案例
下面,我们以豆豆荚网站中“视频”类别下的App信息为例,通过上述两种方式,抓取该类别下的所有App信息,包括App名称、评论、安装数、和音量。
先简单分析一下网站,可以看到页面是通过ajax加载的,GET请求自带了一些参数,可以使用params参数构造URL请求,但是不知道怎么做有很多页面。为了保证所有页面都被下载,设置更大的页面数,比如100页甚至1000页。
下面我们尝试使用For和While循环爬取。
要求
▌For 循环
主要代码如下:
类 Get_page():
def __init__(self):
#ajax请求地址
self.ajax_url ='#39;
def get_page(self,page,cate_code,child_cate_code):
参数 = {
'catId': cate_code,
'subCatId':child_cate_code,
“页面”:页面,
}
response = requests.get(self.ajax_url, headers=headers, params=params)
content = response.json()['data']['content'] #在json中提取html页面数据
返回内容
def parse_page(self, content):
# 解析网页内容
内容 = pq(content)('.card').items()
数据 = []
对于内容中的内容:
数据 1 = {
'app_name': content('.name').text(),
'安装':内容('.安装计数')。文本(),
'volume': content('.meta span:last-child').text(),
'评论':内容('.comment')。文本(),
}
数据.附加(数据1)
如果数据:
# 写入MongoDB
self.write_to_mongodb(数据)
如果 __name__ =='__main__':
# 实例化数据抽取类
wandou_page = Get_page()
cate_code = 5029 # 视频播放的大类号
child_cate_code = 716 # 视频类别号
对于范围内的页面 (2, 100):
打印('*' * 50)
打印('爬行:页面 %s'% 页)
content = wandou_page.get_page(page,cate_code,child_cate_code)
# 添加循环判断,如果内容为空,表示页面已下载,跳出循环
如果不是内容 =='':
wandou_page.parse_page(内容)
sleep = np.random.randint(3,6)
时间.睡眠(睡眠)
别的:
print('该分类的最后一页已下载')
休息
在这里,首先创建了一个 Get_page 类。get_page 方法用于获取Response 返回的json 数据。通过网站解析json后,发现要提取的内容是data字段下的content key包裹的一段html文本。使用parse_page方法中的pyquery函数进行解析,最终提取出App名称、评论、安装数、体积这四个信息来完成爬取。
在main函数中,if函数用于条件判断。如果内容不为空,则表示页面有内容,则循环往下爬,如果为空,则表示页面已经被爬取,执行else分支下的break语句。结束循环并完成爬行。
爬取结果如下,可以看到该分类下共爬取了41页信息。
▌While 循环
while循环和For循环的思路大致相同,但是有两种写法,一种还是结合break语句,一种是改变判断条件。
整体代码不变,只修改For循环部分:
page = 2 # 设置开始爬取的页面数
而真:
打印('*' * 50)
打印('爬行:页面 %s' %page)
content = wandou_page.get_page(page,cate_code,child_cate_code)
如果不是内容 =='':
wandou_page.parse_page(内容)
页 += 1
sleep = np.random.randint(3,6)
时间.睡眠(睡眠)
别的:
print('该分类的最后一页已下载')
休息
或者:
page = 2 # 设置开始爬取的页面数
page_last = False # while 循环初始条件
虽然不是 page_last:
#...
别的:
# 休息
page_last = True # 将 page_last 改为 True 跳出循环
结果如下,可以看到For循环的结果是一样的。
我们可以测试其他分类下的网页,例如选择“K歌”分类,代码为:718,然后只需要相应修改main函数中的child_cate_code,再次运行程序,即可看到下一个此类别共抓取 32 页。
由于Scrapy中的写入方式与Requests略有不同,所以接下来我们将再次在Scrapy中实现两种循环爬取方式。
刮痧
▌For 循环
在Scrapy中使用For循环递归爬取的思路很简单,就是先批量生成所有请求的URL,包括最后一个无效的URL,然后在parse方法中加入if来判断和过滤无效请求,然后抓取所有页面。由于Scrapy依赖Twisted框架,采用异步请求处理方式,也就是说Scrapy在发送请求的同时解析内容,所以这样会发送很多无用的请求。
def start_requests(self):
页数=[]
对于范围内的 i(1,10):
网址='%s'%i
page = scrapy.Request(url,callback==self.pare)
pages.append(页面)
返回页面
下面,我们选择豌豆荚“新闻阅读”类别下的“电子书”App页面信息,使用For循环尝试爬取。主要代码如下:
def start_requests(self):
cate_code = 5019 # 新闻阅读
child_cate_code = 940 # 电子书
打印('*' * 50)
页数 = []
对于范围内的页面(2,50):
打印('爬行:页面 %s' %page)
参数 = {
'catId': cate_code,
'subCatId':child_cate_code,
“页面”:页面,
}
category_url = self.ajax_url + urlencode(params)
pa = 产出scrapy.Request(category_url,callback=self.parse)
pages.append(pa)
返回页面
定义解析(自我,响应):
if len(response.body) >= 100: #判断页面是否被爬取,该值设置为100,因为响应的长度为87时没有内容
jsonresponse = json.loads(response.body_as_unicode())
内容 = jsonresponse['数据']['内容']
#响应为json,json内容为html,html为文本不能直接用.css提取,必须先转换
内容=scrapy.Selector(文本=内容,类型=“html”)
content = contents.css('.card')
对于内容中的内容:
item = 玩豆家Item()
item['app_name'] = content.css('.name::text').extract_first()
item['install'] = content.css('.install-count::text').extract_first()
item['volume'] = content.css('.meta span:last-child::text').extract_first()
item['comment'] = content.css('.comment::text').extract_first().strip()
产量项目
上面的代码简单易懂,简单说明几点:
一、 判断当前页面是否被爬取的判断条件改为response.body的长度大于100。
因为请求已经爬取完成页面,返回的响应结果不是空的,而是一段json内容的长度(长度为87),这里的内容key值内容为空,所以选择判断条件这里)一个大于87的值就足够了,比如100,大于100表示这个页面有内容,小于100表示该页面已经被抓取。
{"state":{"code":2000000,"msg":"Ok","tips":""},"data":{"currPage":-1,"content":""}}
二、 当需要从文本中解析内容时,无法直接解析,需要先进行转换。
正常情况下,我们在解析内容的时候直接解析返回的响应,比如使用response.css()方法,但是这里,我们的解析对象不是响应,而是响应返回的json内容中的html文本。文本无法直接使用.css()方法进行解析,所以在解析html之前,需要在解析前添加如下代码行进行转换。
内容=scrapy.Selector(文本=内容,类型=“html”)
结果如下。您可以看到所有 48 个请求都已发送。事实上,这个类别的内容只有22页,也就是发送了26个不必要的请求。
▌While 循环
接下来,我们使用While循环再次尝试抓取,代码省略了与For循环中相同的部分:
def start_requests(self):
page = 2 # 设置开始爬取的页面数
dict = {'page':page,'cate_code':cate_code,'child_cate_code':child_cate_code} # 元传递参数
产生scrapy.Request(category_url,callback=self.parse,meta=dict)
定义解析(自我,响应):
if len(response.body) >= 100: #判断页面是否被爬取,设置为100,因为没有内容时长度为87
page = response.meta['page']
cate_code = response.meta['cate_code']
child_cate_code = response.meta['child_cate_code']
#...
对于内容中的内容:
产量项目