1. 为爱出码
就在昨天,有个朋友忽然给我发了一条私信: 麦叔,想看笑话吗?
我说:看你的笑话啊!有什么好笑的? 他说:不是,我发现一个笑话网站,上面有很多笑话! 我说:然后呢? 他说:那个网站程序员好像比较笨,我想写个程序把笑话都抓下来,每天给女朋友发送1个笑话! 我说:哦,你去抓吧。我没有女朋友,不需要。 2. 笨程序员在他的死皮赖脸的央求下,我去看了一下那个笑话网站的页面: http://xiaohua.zol.com.cn/detail1/1.html 这个网站果然是有漏洞的: - 首先网站没有使用https,这个现代网站的基本标配都没有。 - 然后它的URL很容易被猜测,你看1.html,那是不是有2.html呢?试了一下还真有。 这就简单了,要抓它就顺藤摸瓜,1,2,3...100000抓下去就是了。
可以说这个网站的爬虫防守几乎没做。 3. 简单爬虫几分钟后,他乐呵呵的哭丧着脸又来找我。 对,没错!他乐呵呵的是因为很快就写好了爬虫,也抓取了一些笑话。哭伤着脸是因为抓了没几下程序就挂了。 来看看他的程序: import requests import bs4
url = 'http://xiaohua.zol.com.cn/detail1' with open('joke.txt') as f: for joke_id in range(1, 100000): response = requests.get(f'{url}{joke_id}.html') soup = bs4.BeautifulSoup(response.text, 'lxml') joke_text = soup.select('div.article-text')[0].getText().strip() f.write(f'{joke_id}, {joke_text}\n')
他代码写的还算简洁: - 使用requests.get抓取网页内容,动态拼接网页的URL,都要感谢网站程序员的没防御啊。
- 使用BeautifulSoup把笑话的文本解析出来。
- 保存到joke.txt中。 好家伙,这一口气要抓10万个笑话,你有几个女朋友啊??
表面上程序还行,但我用我的不太近视的近视眼瞄了一眼,就知道这个程序一定活不了多久,你想想看问题在哪里。 4. 得优化上面的程序在电视剧里顶多活一集,因为如果任何一个网络请求报错了,这个程序就挂啦!网络请求报错是很正常的事情,很多原因都可能会造成网络请求失败! 这得改,必须得改! import requests import bs4
url = 'http://xiaohua.zol.com.cn/detail1' with open('joke.txt') as f: for joke_id in range(1, 100000): try: response = requests.get(f'{url}{joke_id}.html') soup = bs4.BeautifulSoup(response.text, 'lxml') joke_text = soup.select('div.article-text')[0].getText().strip() f.write(f'{joke_id}, {joke_text}\n') except Exception as e: print('笑话没抓到,继续抓下一个')
通过把网络请求放到try except中,如果请求出错了,只会打印一句“笑话没抓到,继续抓下一个',至少程序不会停掉! 这货肯定活的的稳稳的! 但是,你活得太久了也不行啊。这10万条数据,你得抓多久啊!女朋友要说:你不行! 这得改,必须得改! 5. 多线程这还不好改,用多线程: import requests import bs4 import threading
url = 'http://xiaohua.zol.com.cn/detail1' def get_joke(joke_id, file): response = requests.get(f'{url}{joke_id}.html') soup = bs4.BeautifulSoup(response.text, 'lxml') joke_text = soup.select('div.article-text')[0].getText().strip() file.write(f'{joke_id}, {joke_text}\n')
with open('joke.txt') as f: for joke_id in range(1, 100000): try: threading.Thread(target=get_joke, args=(joke_id,)) except Exception as e: print('笑话没抓到,继续抓下一个')
代码说明: 运行一下看看,应该没问题。可是,他的电脑爆啦!!! 因为短时间内启动了太多的线程。 这个得控制一下,这个必须控制。 6. 线程池简单,使用线程池,控制线程的个数: import requests import bs4 import concurrent.futures
url = 'http://xiaohua.zol.com.cn/detail1' def get_joke(joke_id, file): response = requests.get(f'{url}{joke_id}.html') soup = bs4.BeautifulSoup(response.text, 'lxml') joke_text = soup.select('div.article-text')[0].getText().strip() file.write(f'{joke_id}, {joke_text}\n')
with open('joke.txt') as f: with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor: for joke_id in range(1, 100000): try: executor.submit(get_joke, joke_id) except Exception as e: print('笑话没抓到,继续抓下一个')
代码说明: - 把每个抓取任务提交给线程池,最多100个线程干活,多余得排队
...几小时后...终于大功告成! 感觉,学会编程还真挺好的! 7. 结果几天以后,他又来找我,像是霜打的茄子,原来女朋友和他分手了! 他说:没想到,到头来,我就是个笑话。 我说:没想到,真看了你的笑话。是怎么回事? 原来,他没有人工审核笑话,结果程序随机给女朋友发了3个笑话就结束了他们的友谊: 2020-11-17:问:女朋友丑怎么办? 答:丑不是她的错,都是爹妈给的,你想让她漂亮就去韩国整容,如果你没钱,你还嫌她丑就是你的错,要么接受要么换。 2020-11-18:如果我们分手,白天倒还好,可一到晚上就再也抑制不了内心的感情,一个人蒙在被子里偷偷地笑了起来。 2020-11-19日:今天和女朋友分手了,总是有着那么的一些事情让人心疼。 经过内心的挣扎,我终于鼓起勇气拨通了电话:“喂,是移动吗?嗯,是这样的,我和我女朋友分手了,我前天帮她冲了两百话费,你能帮我把它要回来吗?” 8. 后记:爬虫程序还有改进的空间: - 这么大量的线程来自同一个IP地址,8成会被封掉的,可以考虑使用动态换代理,防止IP被封
如果你是那个开发网站的程序员,你要怎么防御: - 网址不要用很容易猜测的数字作为笑话的编号,使用随机生成的长字符串,你看看淘宝的网址你就知道了。
- 连续访问10次,出验证码,验证通过了才能继续访问。
如果本文阅读上千,大家又有需求,我们可以再说说这几个。 最后,我劝年轻人耗子尾汁,不管是开发网站,开始开发爬虫,都要讲武德,不要大意! 点赞,转发,点在看,就是给我最大的帮助,谢谢!
|