找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 298578|回复: 0

程序员不讲武德,为女朋友乱用多线程!

[复制链接]

该用户从未签到

发表于 2021-1-23 01:02:38 | 显示全部楼层 |阅读模式

您需要 登录 才可以下载或查看,没有账号?立即注册

×
来源:麦叔编程
作者:麦叔
1. 为爱出码

就在昨天,有个朋友忽然给我发了一条私信:
麦叔,想看笑话吗?
我说:看你的笑话啊!有什么好笑的?
他说:不是,我发现一个笑话网站,上面有很多笑话!
我说:然后呢?
他说:那个网站程序员好像比较笨,我想写个程序把笑话都抓下来,每天给女朋友发送1个笑话!
我说:哦,你去抓吧。我没有女朋友,不需要。
2. 笨程序员

在他的死皮赖脸的央求下,我去看了一下那个笑话网站的页面:
这个网站果然是有漏洞的:
- 首先网站没有使用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')

他代码写的还算简洁:
[list,
[*,使用requests.get抓取网页内容,动态拼接网页的URL,都要感谢网站程序员的没防御啊。
[*,使用BeautifulSoup把笑话的文本解析出来。
[*,保存到joke.txt中。 好家伙,这一口气要抓10万个笑话,你有几个女朋友啊??
[/list,表面上程序还行,但我用我的不太近视的近视眼瞄了一眼,就知道这个程序一定活不了多久,你想想看问题在哪里。
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('笑话没抓到,继续抓下一个')
代码说明:
[list,
[*,引入了threading模块
[*,把抓取笑话的代码放到一个函数中
[*,给每个笑话创建独立的线程去抓取
[/list,运行一下看看,应该没问题。可是,他的电脑爆啦!!!
因为短时间内启动了太多的线程。
这个得控制一下,这个必须控制。
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('笑话没抓到,继续抓下一个')

代码说明:
[list,
[*,引入了concurrent模块
[*,创建了一个线程池,最多100个线程
[*,把每个抓取任务提交给线程池,最多100个线程干活,多余得排队
[/list,...几小时后...终于大功告成!
感觉,学会编程还真挺好的!
7. 结果

几天以后,他又来找我,像是霜打的茄子,原来女朋友和他分手了!
他说:没想到,到头来,我就是个笑话。
我说:没想到,真看了你的笑话。是怎么回事?
原来,他没有人工审核笑话,结果程序随机给女朋友发了3个笑话就结束了他们的友谊:
2020-11-17:

问:女朋友丑怎么办?
答:丑不是她的错,都是爹妈给的,你想让她漂亮就去韩国整容,如果你没钱,你还嫌她丑就是你的错,要么接受要么换。
2020-11-18:

如果我们分手,白天倒还好,可一到晚上就再也抑制不了内心的感情,一个人蒙在被子里偷偷地笑了起来。
2020-11-19日:

今天和女朋友分手了,总是有着那么的一些事情让人心疼。
经过内心的挣扎,我终于鼓起勇气拨通了电话:“喂,是移动吗?嗯,是这样的,我和我女朋友分手了,我前天帮她冲了两百话费,你能帮我把它要回来吗?”
8. 后记:

爬虫程序还有改进的空间:
[list,
[*,这么大量的线程来自同一个IP地址,8成会被封掉的,可以考虑使用动态换代理,防止IP被封
[*,可以使用协程进一步提高效率。
[/list,如果你是那个开发网站的程序员,你要怎么防御:
[list,
[*,网址不要用很容易猜测的数字作为笑话的编号,使用随机生成的长字符串,你看看淘宝的网址你就知道了。
[*,防止某一个IP短时间内过多访问你的网站。
[*,连续访问10次,出验证码,验证通过了才能继续访问。
[/list,如果本文阅读上千,大家又有需求,我们可以再说说这几个。
最后,我劝年轻人耗子尾汁,不管是开发网站,开始开发爬虫,都要讲武德,不要大意!
支持麦叔,点赞,转发,点在看,就是给我最大的帮助,谢谢!
回复

使用道具 举报

网站地图|页面地图|文字地图|Archiver|手机版|小黑屋|找资源 |网站地图

GMT+8, 2024-11-23 20:27

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表