前言:有些时候,我们在查一些比较老的数据,由于网站升级,或者改版之类的,我们无法得到我们想要数据的网址,也就无法爬取数据,下面,我将带大家一起来分析一下自己的案例。
明确需求
背景:在网上搜自己的名字,居然找到一份文档,大概是这样的样子2015年普通高校招生录取名单公示(五) (sxkszx.cn)
然后就顺腾摸瓜,想找到全部的数据——2015年全部的数据查找数据
刚开始,由于找到了网站,2015年普通高校招生录取名单公示(五) (sxkszx.cn),网址是http://www.sxkszx.cn/
,我想在网站搜索,但是发现网站没有搜索功能,而且,更要命的是,这个每一页,没有上一页,下一页的链接,这样我们就找不到相关联的数据。
而且,我们发现,网址的拼接也是很奇怪,http://www.sxkszx.cn/news/2015722/n82552675.html,news代表新闻,2015722代表发布日期,后面的n82552675就很奇怪,如果改成n82552676则会404,也就是无法单纯的修改最后一位来完成地址的模拟-
使用搜索引擎增强搜索力度
现在进入了困境,我们无法在该网站查找。不过我们可是使用
2015年普通高校招生录取名单 site:sxkszx.cn
来进行网站的查找,这样是使用搜索引擎来进行该网站的搜索,在百度地址栏输入搜索即可。
然后我们会找到
很多数据被找到了,然后我们分析一下,发现其中缺少了一些公示,这意味这数据不太完整。 -
网站地址分析 + 试探
现在问题又回到了原点,我们不知道网页的网址,那么就无法爬取数据。但是,现在要比最开始好很多,因为现在除了录取名单五以外,还多了很多,以供我们分析。- 整理一下,来分析一下数据的异同
为了能够看出其中的异同,我把数据一行一行列了出来,然后行号就是公示名单的数字标号(空缺部分表示目前未知的网站链接)
现在分析一下网址,我们发现,最后四位数字好像是递增的
对,这有点像是,新闻在数据库中存储的编号id,意味着这是第2669篇新闻,那么,第2670篇新闻的网址应该是怎样的呢?
推测如下
http://www.sxkszx.cn/news/日期/n四位不知名数字2670
且日期应当是在2015.7.22当天,或者之后
讲道理,我们这里应该要分析 四位不知名数字 的含义,或者讨论它是如何生成的,但是笔者想了很多,都没能猜到,之前猜测或者与时间/文章名字/等等,但是都不太对,总之没能猜到。
- 由于我们没猜到,所以我们有一个最朴素的想法,我们遍历0000-9999不就可以了吗?找到那个可以正确返回的链接
对,这个想法很正确。
下面的这一段代码,返回了一个列表,列表中包含了从0000-9999的所有可能的网址链接
def gene_urls():
base_url = "http://www.sxkszx.cn/news/"
date = "2015820"
newsId = "2745"
urls = []
for i in range(1, 10000):
s = str(i).zfill(4)
url = base_url + date + "/n" + s + newsId + ".html"
urls.append(url)
return urls
写到这里,大家可能觉得,这不很简单吗?直接
for url in urls:
response = requests.get(url)
if response.status.code == 200:
print url
break
但是,这是很朴素的单线程,我们做一个计算
我们大概空缺的是
日期 新闻id
722 2675
723 2676
...
83
...
2696
我们发现,一次尝试,都要花费很长的时间,如果我们想要全部试探,需要很长的时间,且,requests要不断断开,建立连接,花销很大。
下面,我们尝试多线程。
下面是一份很长的代码,大家不用关心具体怎么实现的,只要知道
single_thread 代表单线程
multi_thread 代表多线程
即可
# 不断尝试url,当返回不是404,则加入my_urls.txt中
import requests
import threading
import requests.adapters
import time
base_url = "http://www.sxkszx.cn/news/"
date = "2015722"
newsId = "2678"
# f = open("my_urls.txt", 'w+')
flag = 0
# count = 0
def gene_urls():
urls = []
for i in range(1, 10000):
s = str(i).zfill(4)
url = base_url + date + "/n" + s + newsId + ".html"
urls.append(url)
return urls
def test_url(url):
# response = requests.get(url=url, timeout=30)
# if response.status_code == 200:
# print(url)
# try:
# with requests.get(url, timeout=5) as r:
# if r.status_code == 200:
# flag = 1
# print(url)
# except:
# print("处理异常中...")
# time.sleep(5)
global flag
if flag == 0:
response = None
global count
try:
# 设置重连次数
requests.adapters.DEFAULT_RETRIES = 5
s = requests.session()
# 设置连接活跃状态为False
s.keep_alive = False
response = requests.get(url, stream=False, timeout=10)
if response.status_code == 200:
flag = 1
print(url)
# 关闭请求 释放内存
response.close()
del (response)
except Exception as indentfier:
time.sleep(5)
def single_thread(need_test_urls):
for url in need_test_urls:
global flag
if flag == 0:
test_url(url)
else:
break
def multi_thread(need_test_urls):
threads = []
for url in need_test_urls:
threads.append(
threading.Thread(target=test_url, args=(url,))
)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("ok")
if __name__ == "__main__":
u = gene_urls()
multi_thread(u)
# single_thread(u)
但是问题到这里还没有结束,尽管编写上面的代码,已经踩了很多的坑,包括 如requests未断开;未手动断开(采用with 语句,自动断开会导致他会慢慢断开,还是会报错,max tries,服务器中止...);
但是新的问题又来了,我发现这个代码在有的 日期 + 新闻id上可以运行出结果来,有的则不会,具体原因我也不太清楚,评论区的小伙伴有知道的,可以告诉一下我,谢谢~
- scrapy框架的使用
最后的最后,我实在受不了了,我大概明白,应该是并发的时候,导致requests的连接无法断开,或者断开需要时间,但是还没有断开的这个期间,新的请求已经发送了,这样就会有一部分请求是没有效果的,也就是一部分链接被卡掉了。
而这个,是目前的我所解决不了的。
于是我想着试一下scrapy,因为我之前听过scrapy,但是没有用过,想着用它来解决并发问题,会不会好点。
先说实验结果,最终成功解决了并发问题,对于每一个 日期 + 新闻id 所产生的链接列表,大概在1mins内可以得到结果,已经算是很快了。
再说下去,就要讲scrapy框架了,但是现在我好饿,我先去吃个饭饭,宝宝饿了