php 循环抓取网页内容(PythonFor和While循环不确定页数的网页需要学习的地方)

优采云 发布时间: 2021-11-24 22:20

  php 循环抓取网页内容(PythonFor和While循环不确定页数的网页需要学习的地方)

  本文转载自以下网站:Python For和While循环爬取页数不确定的网页

  学习的地方

  有两种方法。

  第一种方法使用带有 break 语句的 For 循环。最后一页的页数设置为更大的参数,足以循环遍历所有页面。当爬行完成时,中断跳出循环并结束爬行。

  第二种方法使用While循环,可以和break语句结合使用,也可以将初始循环判断条件设置为True,从头爬到最后一页,然后将判断条件改为False进行跳转退出循环并结束爬行。

  Requests 和 Scrapy 分别使用 For 循环和 While 循环来抓取页数不确定的网页。

  摘要:Requests 和 Scrapy 分别使用 For 循环和 While 循环来抓取页面数量不确定的网页。

  我们通常遇到的网站页码的显示形式有以下几种:

  一是将所有页码可视化显示,比如之前爬取的宽和东方财富。

  文章见:

  ∞ Scrapy爬取并分析了关的6000个app,发现良心软了

  ∞ 50行代码爬取东方财富网百万行财务报表数据

  

  二是不直观显示网页总数,只能在后台查看。比如之前爬过的虎嗅网络,见文章:

  ∞ pyspider 爬取并分析 50,000 个老虎嗅探网文章

  

  第三个是我今天要讲的。不知道有多少页,比如豌豆荚:

  

  对于前两种形式的网页,爬取方法很简单,只需要使用For循环从第一页爬到最后一页即可。第三种形式不适用,因为不知道最后一页的编号,所以无法判断循环到哪一页结束。

  如何解决?有两种方法。

  第一种方法使用带有 break 语句的 For 循环。最后一页的页数设置为更大的参数,足以循环遍历所有页面。当爬行完成时,中断跳出循环并结束爬行。

  第二种方法使用While循环,可以和break语句结合使用,也可以将初始循环判断条件设置为True,从头爬到最后一页,然后将判断条件改为False进行跳转退出循环并结束爬行。

  实际案例

  下面,我们以豆豆荚网站中“视频”类别下的App信息为例,通过上述两种方式,抓取该类别下的所有App信息,包括App名称、评论、安装数、和音量。

  首先简单分析一下网站,可以看到页面是通过ajax加载的,GET请求自带了一些参数,可以使用params参数构造一个URL请求,但是我没有知道总共有多少页。为了保证所有页面都被下载,设置更大的页面数,比如100页甚至1000页。

  下面我们尝试使用For和While循环爬取。

  

  请求▌For 循环

  主要代码如下:

  class Get_page():

def __init__(self):

# ajax 请求url

self.ajax_url = \'https://www.wandoujia.com/wdjweb/api/category/more\'

def get_page(self,page,cate_code,child_cate_code):

params = {

\'catId\': cate_code,

\'subCatId\': child_cate_code,

\'page\': page,

}

response = requests.get(self.ajax_url, headers=headers, params=params)

content = response.json()[\'data\'][\'content\'] #提取json中的html页面数据

return content

def parse_page(self, content):

# 解析网页内容

contents = pq(content)(\'.card\').items()

data = []

for content in contents:

data1 = {

\'app_name\': content(\'.name\').text(),

\'install\': content(\'.install-count\').text(),

\'volume\': content(\'.meta span:last-child\').text(),

\'comment\': content(\'.comment\').text(),

}

data.append(data1)

if data:

# 写入MongoDB

self.write_to_mongodb(data)

if __name__ == \'__main__\':

# 实例化数据提取类

wandou_page = Get_page()

cate_code = 5029 # 影音播放大类别编号

child_cate_code = 716 # 视频小类别编号

for page in range(2, 100):

print(\'*\' * 50)

print(\'正在爬取:第 %s 页\' % page)

content = wandou_page.get_page(page,cate_code,child_cate_code)

# 添加循环判断,如果content 为空表示此页已经下载完成了,break 跳出循环

if not content == \'\':

wandou_page.parse_page(content)

sleep = np.random.randint(3,6)

time.sleep(sleep)

else:

print(\'该类别已下载完最后一页\')

break

  在这里,首先创建了一个 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 # 设置爬取起始页数

while True:

print(\'*\' * 50)

print(\'正在爬取:第 %s 页\' %page)

content = wandou_page.get_page(page,cate_code,child_cate_code)

if not content == \'\':

wandou_page.parse_page(content)

page += 1

sleep = np.random.randint(3,6)

time.sleep(sleep)

else:

print(\'该类别已下载完最后一页\')

break

  或者:

  page = 2 # 设置爬取起始页数

page_last = False # while 循环初始条件

while not page_last:

#...

else:

# break

page_last = True # 更改page_last 为 True 跳出循环

  结果如下,可以看到For循环的结果是一样的。

  

  我们可以测试其他类别下的网页,例如选择“K歌”类别,代码为:718,然后只需要相应地修改main函数中的child_cate_code,再次运行程序,就可以看到下一次共抓取此类别 32 个页面。

  

  由于Scrapy中的写入方式与Requests略有不同,所以接下来我们将在Scrapy中再次实现两种循环爬取方式。

  Scrapy▌For 循环

  在Scrapy中使用For循环递归爬取的思路很简单,就是先批量生成所有请求的URL,包括最后一个无效的URL,然后在parse方法中加入if来判断和过滤无效请求,然后抓取所有页面。由于Scrapy依赖于Twisted框架,所以采用了异步请求处理方式,也就是说Scrapy在发送请求的同时解析内容,所以会发送很多无用的请求。

  def start_requests(self):

pages=[]

for i in range(1,10):

url=\'http://www.example.com/?page=%s\'%i

page = scrapy.Request(url,callback==self.pare)

pages.append(page)

return pages

  下面,我们选择豌豆荚“新闻阅读”类别下的“电子书”App页面信息,使用For循环尝试爬取。主要代码如下:

  def start_requests(self):

cate_code = 5019 # 新闻阅读

child_cate_code = 940 # 电子书

print(\'*\' * 50)

pages = []

for page in range(2,50):

print(\'正在爬取:第 %s 页 \' %page)

params = {

\'catId\': cate_code,

\'subCatId\': child_cate_code,

\'page\': page,

}

category_url = self.ajax_url + urlencode(params)

pa = yield scrapy.Request(category_url,callback=self.parse)

pages.append(pa)

return pages

def parse(self, response):

if len(response.body) >= 100: # 判断该页是否爬完,数值定为100是因为response无内容时的长度是87

jsonresponse = json.loads(response.body_as_unicode())

contents = jsonresponse[\'data\'][\'content\']

# response 是json,json内容是html,html 为文本不能直接使用.css 提取,要先转换

contents = scrapy.Selector(text=contents, type="html")

contents = contents.css(\'.card\')

for content in contents:

item = WandoujiaItem()

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()

yield item

  上面的代码简单易懂,简单说明几点:

  一、 判断当前页面是否被爬取的判断条件改为response.body的长度大于100。

  因为请求已经爬取完成页面,返回的响应结果不是空的,而是一段json内容的长度(长度为87),其中content key value content为空,所以选择判断条件这里)一个大于87的值就足够了,比如100,即如果大于100,则表示该页面有内容,如果小于100,则表示该页面已被抓取.

  {"state":{"code":2000000,"msg":"Ok","tips":""},"data":{"currPage":-1,"content":""}}

  二、 当需要从文本中解析内容时,无法直接解析,需要先进行转换。

  正常情况下,我们在解析内容的时候直接解析返回的响应,比如使用response.css()方法,但是这里,我们的解析对象不是响应,而是响应返回的json内容中的html文本。文本不能直接使用.css()方法解析,所以在解析html之前,需要在解析前添加如下代码行。

  contents = scrapy.Selector(text=contents, type="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} # meta传递参数

yield scrapy.Request(category_url,callback=self.parse,meta=dict)

def parse(self, response):

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\']

#...

for content in contents:

yield item

# while循环构造url递归爬下一页

page += 1

params = {

\'catId\': cate_code,

\'subCatId\': child_cate_code,

\'page\': page,

}

ajax_url = self.ajax_url + urlencode(params)

dict = {\'page\':page,\'cate_code\':cate_code,\'child_cate_code\':child_cate_code}

yield scrapy.Request(ajax_url,callback=self.parse,meta=dict)

  在此,简单说明几点:

  一、While循环的思路是从头开始爬取,使用parse()方法解析,然后递增页数构造下一页的URL请求,然后循环解析直到最后一页被抓取。, 这不会像 For 循环那样发送无用的请求。

  二、parse()方法在构造下一页请求时需要用到start_requests()方法中的参数,可以使用meta方法传递参数。

  运行结果如下,可以看到请求数正好是22,完成了所有页面的App信息爬取。

  

  以上就是本文的全部内容,总结一下:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线