本次文章主要以例子来讲解爬虫进阶一点点的网站。
这个博主很懒,经常拖更。
入门文章看这个 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
前言
提示:这次的内容可能会稍微比上一篇文章难一丢丢,但是不涉及前端太深奥的东西,这些会在下一弹展示。
提示:以下是本篇文章正文内容。
一、游戏新闻中心
网址:http://news.17173.com
-
第一步还是先看一看网页结构,找一找我们要爬取的数据在什么地方。
-
先按F12打开开发者模式 然后点击刷新网页的按钮
-
观察并找到是否有我们想要的数据,这里有三个包,如果好奇的话可以一个个点进去看。
-
一般想这种带有List的都是带有我们数据的。如果你经常看网站也会得到这样的结论。或者你可以看一下文件的后缀。这里有js和jsonp,js大家应该都熟悉、jsonp(JSON with Padding) 是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域获取资料。
-
我们双击一下这个包
-
我们会发现这很有可能就是我们想要的数据,但是太杂乱了。
-
看到这大家可能觉得有点眼熟,这玩意跟JSON也太像了吧?我们把这个粘贴到JSON在线解析去。大家会发现好像有点不太对劲。我们先去掉这个括号前面的东西,删掉文末最后一个括号。
-
这样就正常了
-
但我们得到这个数据之后有两种办法来获取我们想要的东西。
-
1、用代码将刚刚叙述的东西删除,然后转换成JSON的格式进行获取。但这个我尝试了很久很久很久。最终也没有成功。不过小伙伴们可以尝试一下~ (Python字符串转字典, 成功之后可以评论区留言~互相学习)
-
2、就是一点点切分了,找到我们想要的东西。
-
-
我使用的是第二种方法,只好掏出正则这个神器了。怎么用呢?就是用正则匹配出标题和新闻的正文网址。经过观察我们就可以写出正则表达式。写正则偷懒的方法就是复制你要匹配的东西,把你要匹配的东西换成(.*?),这里pageUrl是必须的,title也是必须的。而括号里面是我需要的。它的意思是匹配任意。所以当你想匹配其他任意(但这些不是你所需要的)也可以使用它,只不过这次不要带上括号。
# 用正则来匹配url和标题
com1 = re.compile(r'"pageUrl":"(.*?)"', re.S)
com2 = re.compile(r'"title":"(.*?)"', re.S)
- 我们再看看请求头
- 发现这里的参数确实有点多,我们先往下多刷出点一样的包来观察一下。
- 发现只有"pageNo"和"_"这两个参数是改变的
- pageNo大概猜测一下就是页码数。
- 但这个"_"是什么鬼?我本来正在烧脑中但是发现不过也就是从43变成了44,那下一个包是不是45?
- 这印证了我的观点,也就是说你访问下一个包时这个参数是要比前一个参数大1的。
- 在获得了新闻文本url后该怎么做呢?
- 老规矩按F12,然后开始找我们要的东西
- 找到了。这里就可以采用之前的xpath进行爬取了。但是有许多个部分被包在了不同的div标签里面。这个时候只需要到上一层级去,并"获取下层级所有文本内容即可"
//text() : 获取所有的文本内容
- 代码如下:
import requests
from lxml import etree
import pandas as pd
import re
if __name__ == '__main__':
com1 = re.compile(r'"pageUrl":"(.*?)"', re.S) # 用正则来匹配url和标题
com2 = re.compile(r'"title":"(.*?)"', re.S)
# 持久化存储用
l_main = []
# 网址
url = "http://interface.17173.com/content/list.jsonp?"
# 必要的参数
params = {
"callback": "jQuery1111044137195735135837_1625278587501",
"categoryIds": "10019,10152,10161",
"pageSize": "21",
"pageNo": "1",
"_": "16252785875",
}
count = 0
headers = {
"Referer": "http://news.17173.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/89.0.4389.82 Safari/537.36 "
}
for j in range(2, 100): # 循环爬取数据
count += 1
params['pageNo'] = str(j) # 参数会改变
if count < 10: # 这里也是, 小于10的时候和大于了是不一样的
params['_'] = params['_'] + '0' + str(count)
else:
params['_'] = params['_'] + str(count)
res = requests.get(url=url, headers=headers, params=params)
data = "".join(res.text.rstrip(")").split("(")[1:]) # 删去右侧的括号以及左侧不需要的部分
data_list_all_ = "".join(data.split(":[")[1:]) # 删除左边不需要的部分
data_list_all = data_list_all_.split("}")
for i in data_list_all:
if len(com1.findall(i)) == len(com2.findall(i)) and len(com1.findall(i)) != 0 and len(com2.findall(i)) != 0:
news_url = com1.findall(i)[0]
news_title = com2.findall(i)[0]
news_res = requests.get(url=news_url, headers=headers)
news_res.encoding = "UTF-8" # 设置编码
tree = etree.HTML(news_res.text) # 用xpath进行查找
string = tree.xpath('//div[@class="gb-final-pn-article"]/div[@class="gb-final-mod-article '
'gb-final-mod-article-p2em"]//text()')
string = "".join(string)
string = string.replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "").strip() # 清洗字符串
print(string)
l_group = [string, '游戏', news_title]
l_main.append(l_group)
news_res.close()
print(j, "下载成功!")
res.close()
# 进行持久化存储
df = pd.DataFrame(l_main, index=[i for i in range(len(l_main))], columns=['content', 'channelName', 'title'])
df.to_csv("游戏新闻.csv")
二、房产要闻-和讯网
网址:http://house.hexun.com/list/
- 同样的还是先看看网页结构
- 我认为可能是点击完成后再进行动态加载的
- 果不其然是这样的,而且数据的返回形式和第一个网页也是一样的
- 来看看请求头
- 好像有点复杂,我们对照这下一个包看看。
- 这里的cp是表示第几张页面
- 除了callback,其他参数并没有发生改变
- 仔细观察可以发现hx_json后面的第一个数字应该表示的是页码,我们可以再点到第三页看看。
- 显而易见是这样的,而且中间有一段是相同的,但最后几位并不相同。
- 这几位有可能经过了是经过加密处理而生成的,当然也有可能就是随机数。
- 运行之后发现成功!说明并没有想象之中的复杂
- 代码如下
import requests
from lxml import etree
import pandas as pd
import re
if __name__ == '__main__':
com1 = re.compile(r'"entityurl":"(.*?)"', re.S)
com2 = re.compile(r'"title":"(.*?)"', re.S)
l_main = []
url = "http://open.tool.hexun.com/MongodbNewsService/newsListPageByJson.jsp?"
params = {
"id": "100135470",
"s": "50",
"cp": "4",
"priority": "0",
"callback": "hx_json11625273727797"
}
headers = {
"Referer": "http://house.hexun.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/89.0.4389.82 Safari/537.36 "
}
# 拼接
list_ = "hx_json11625273727797".split("1", 1)
for j in range(1, 40):
params['callback'] = str(j).join(list_) # 修改访问参数
res = requests.get(url=url, headers=headers, params=params)
# 下面是进行数据的处理
data = res.text.split("(")[1].split(")")[0].strip().replace("\\", "")
data_list = data.split("[")
data_list_all = data_list[1].split("{")
for i in data_list_all:
# 避免错误数据
if len(com1.findall(i)) == len(com2.findall(i)) and len(com1.findall(i)) != 0 and len(com2.findall(i)) != 0:
news_url = com1.findall(i)[0]
news_title = com2.findall(i)[0] # 正则表达式进行查找
news_res = requests.get(url=news_url, headers=headers)
news_res.encoding = "gb2312" # 设置编码
tree = etree.HTML(news_res.text)
string_list = tree.xpath('//div[@class="art_context"]/div[@class="art_contextBox"]//text()')
string = ",".join(string_list)
string = string.replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "").strip() # 数据处理
l_group = [string, '房产', news_title]
l_main.append(l_group)
news_res.close()
print(j, "下载成功!")
res.close()
# 持久化存储
df = pd.DataFrame(l_main, index=[i for i in range(len(l_main))], columns=['content', 'channelName', 'title'])
df.to_csv("房产新闻.csv")
三、梨视频
网址: https://www.pearvideo.com
- 大致看上去有两种类型:一种是可直接播放,一种是不可直接播放。
- 可直接播放的我们来找一找有没有视频地址。
- 如果找到了视频地址的话,就可以直接对视频地址发送请求。
# 因为图片和视频都是二进制的形式
requests.get(url=url).content
- 但也有没有视频地址的情况
- 这个是没有视频地址的,但是有个传送门,其实也就相当于是视频地址了。我们点击它。发现跳到了这。
- 我们再寻找一下这个视频地址在哪。
- 这就和前面一样了,可以对这个地址发送请求,再用".content"方法
- 整体流程也就清晰了,只不过要做一下url的拼接,小伙伴们去练练手吧~
总结
本文主要介绍了爬虫入门中可能会使用到的一点小方法和技巧。没有涉及过深的前端知识。下一弹会更带有一些深度的前端知识的网页的爬取。加油,小伙伴~