php抓取网页不全 js(数据加载是一种异步加载方式,原始的页面最初不会)
优采云 发布时间: 2022-02-13 07:09php抓取网页不全 js(数据加载是一种异步加载方式,原始的页面最初不会)
数据加载是一种异步加载方式。原创页面起初不收录一些数据。原创页面加载完成后,会请求一个接口从服务器获取数据,然后对数据进行处理并呈现在网页上。只需发送一个 Ajax 请求。如果遇到这样的页面,直接使用requests之类的库爬取原创页面是无法获取到有效数据的。这时候就需要分析网页后端向接口发送的Ajax请求了。如果可以使用requests来模拟Ajax请求,那么就可以成功获取。
Ajax,全称是Asynchronous JavaScript and XML,即异步JavaScript和XML。这不是编程
语言,但使用JavaScript与服务器交换数据并更新,同时保证页面不刷新,页面链接不改变
网页技术的一部分。对于传统的网页,如果要更新其内容,则必须刷新整个页面,但使用 Ajax,您可以在不完全刷新页面的情况下更新其内容。在这个过程中,页面实际上是在后台与服务器交互的。获取到数据后,使用 JavaScript 改变网页,从而更新网页的内容。
以微博为例
以微博为例,切换到微博页面,一直往下滚动,可以发现往下滑动几条微博后就没有再往下,而是出现加载*敏*感*词*,过一会又会出现新的微博继续出现在下方。博客内容,这个过程真的是Ajax加载的过程。注意页面并没有完全刷新,也就是说页面的链接没有变化,但是页面中有新的内容,也就是后面刷新的新微博。这就是通过 Ajax 获取和呈现新数据的方式。
Ajax 实际上有一个特殊的请求类型叫做 xhr。可以找到一个名称以getIndex开头的请求,其Type为xhr,即Ajax请求。用鼠标点击请求,查看请求的详细信息。请求标头中的信息之一是 X-Requested-With:XMLHttpRequest,它将请求标记为 Ajax 请求。然后点击预览查看响应的内容,是 JSON 格式的。
这是一个 GET 类型的请求,请求链接是 . 请求有 4 个参数: type , value ,
containerid 和页面。可以发现,它们的type、value和containerid总是一样的。type总是uid,value的值是页面链接中的数字,其实就是用户的id。另外,还有containerid。可以查到是107603加上用户id。改变的值为page,显然这个参数是用来控制分页的,page=l代表第一页,page=2代表第二页,以此类推。
返回的json数据中最关键的两条信息分别是cardlistinfo和cards:前者收录了比较重要的信息总数,观察后发现其实是微博总数,我们可以估算出页数根据这个数字;另一个是一个列表,里面有10个元素,展开一个看看。
可以发现这个元素有一个比较重要的字段mblog。展开它,可以发现它收录了微博的一些信息,例如态度count(点赞数)、comments_count(评论数)、reposts_count(转发数)、created at(发帖时间)、text(微博的文字) ) 等,都是格式化的内容。
原则
向网页更新发送Ajax请求的过程可以简单分为以下三个步骤:发送请求;解析内容;呈现网页。
发送请求:JavaScript可以实现页面的各种交互功能,Ajax也不例外,JavaScript也能实现。这是由 JavaScript 实现的 Ajax 的最低级别。其实就是新建一个XMLHttpRequest对象,然后调用onreadystatechange属性设置*敏*感*词*,然后调用open和send方法向一个链接(也就是服务器)发送消息。
发送了请求。由于设置了*敏*感*词*器,当服务器返回响应时,会触发onreadystatechange对应的方法,然后可以在该方法中解析响应内容。
解析内容:得到响应后会触发onreadystatechange属性对应的方法,可以通过xmlhttp的responseText属性获取响应内容。返回的内容可能是HTML,也可能是JSON,然后只需要在方法中用JavaScript进一步处理即可。例如,如果是 JSON,则可以对其进行解析和转换。
呈现网页:JavaScript 具有更改网页内容的能力。解析响应内容后,可以调用 JavaScript 对网页进行解析处理。例如,通过document.getElementByid().innerHTML的操作,可以改变一个元素中的源代码,从而改变网页上显示的内容。这种操作也称为DOM操作,即对Document网页的操作。文档操作,如更改、删除等。上例中为 document 。getElementByid("myDiv ”) .innerHTML=xmlhttp.responseText 会将ID为myDiv的节点内部的HTML代码更改为服务器返回的内容,这样服务器返回的新数据就会显示在myDiv元素内部。页面似乎已更新。
代码示例
爬取所有微博的前10页,首先定义一个方法来获取每个请求的结果。在请求时,page 是一个可变参数,所以将它作为方法参数传入。
这里定义 base_url 来表示请求 URL 的前半部分。接下来构造参数字典,其中type、value、containerid为固定参数,page为可变参数。接下来调用urlencode方法将参数转换为-URL GET请求参数,类似于type=ui d&value=2830678474&containerid=78474&page=2。然后,base_url 与参数组合形成一个新的 URL。接下来,我们通过请求请求链接,添加 headers 参数。然后判断响应的状态码,如果是200,直接调用json方法将内容解析成json返回,否则不返回信息。如果发生异常,捕获并输出其异常信息。
以今日头条为例
在抓取之前,先分析一下抓取的逻辑。打开今日头条首页,右上角有一个搜索入口,这里我们尝试捕捉街拍的美图,所以输入“街拍”二字进行搜索。
然后打开开发者工具,查看所有网络请求。首先打开第一个网络请求,这个请求的URL就是当前的link=street shoot。切换到 XHR Filtering 选项卡并查看是否有任何 Ajax 请求。点击数据栏展开,发现有很多条数据。点击第一项展开,可以发现有一个title字段,它的值就是页面中第一条数据的标题。再次查看其他数据,正好是一一对应的。
为了捕捉漂亮的图片,这里的一组图片对应上一个数据字段中的一条数据。每条数据还有一个 image_detail 字段,它是一个列表的形式,其中收录了该组中所有图像的列表。您需要做的就是提出问题并下载它们。一组图片会发布到一个文件夹中,文件夹的名字就是组图的标题。
切换回 Headers 选项卡并观察其请求 URL 和 Headers 信息。您可以看到这是一个 GET 请求。请求 URL 的参数有 offset、format、keyword、autoload、count 和 curtab。
唯一变化的参数是offset,其他参数都没有变化,而且第二次请求的offset值是20,第三次是40,第四次是60,所以可以找到规律,这个offset值是偏移值,然后可以推断出count参数是一次获取的数据条数。因此,我们可以使用offset参数来控制数据分页。
代码示例
实现方法 get_page 以加载单个 Ajax 请求的结果。唯一改变的参数是偏移量,所以我们将它作为参数传递
import requests
from urllib.parse import urlencode
def get_page(offset):
params = {
'offset':offset,
'format':'json',
'keyword':'街拍',
'autoload':'true',
'count':'20',
'cur_tab':'1'
}
url = 'http://www.toutiao.com/search_content/?'+urlencode(params)
try:
response = requests.get(url)
print(response)
if response.status_code == 200:
return response.json()
except requests.ConnectionError:
return None
get_page(1)
实现另一种解析方法:提取每条数据的image_detail字段中的每条图片链接,返回图片链接和图片所属的标题。这时候就可以构造一个*敏*感*词*了
def get_images(json):
if json.get('data'):
for item in json.get('data'):
title = item.get('title')
images = item.get('image_detail')
for image in images:
yield {
'image':image.get('url'),
'title':title
}
这里的数据没有任何价值,说明有防采摘措施。
后面会分析