京公网安备 11010802034615号
经营许可证编号:京B2-20210330
来源:麦叔编程
作者:麦叔
当我慢慢的开在高速公路上,宽敞的马路非常的拥挤!这时候我喜欢让百度导航的小度给我讲笑话,但她有点弱,每次只能讲一个。
百度号称要发力人工智能,成为国内人工智能的领军企业。但从小度的智商和理解能力上,我对此非常怀疑。
所以我们干脆用Python来开发一个可以讲笑话的机器人,可以自由定制功能,想讲几个笑话就讲几个笑话。
由于案例比较综合,整个案例会分成2篇或者3篇文章发出,本文是第一篇,第二篇会隔一天发出。
可以在公众号回复:笑话,获得相关文章的链接。
本文用到以下技术:
为了代码结构清晰,方便维护,我们把代码放到了多个py文件中,每个文件各司其职。
本程序共包括一下几个代码模块:
现在开始写代码,请先创建一个文件夹,建议取名为myjoke。后面所有的代码都在这个文件夹中。
我们使用面向对象的编程思想,创建一个叫做Joke的类,来表示一个笑话。
用了Joke类,代码更清晰,数据传输也更方便。Joke类会被所有其他的模块用到。
创建一个名为joke.py的文件
代码如下:
class Joke: '''
表示一个笑话。
其中title是笑话标题,detail是笑话内容
url是笑话的采集网址,通过url判定笑话是否重复,防止保存重复笑话
id是数据库生成的唯一标识符,刚刚采集下来的笑话是没有id的,所以id可以为空
''' def __init__(self, title, detail, url, id=None):
self.title = title
self.detail = detail
self.url = url
self.id = id
def __str__(self): '''
有了这个方法,print(joke)会把笑话打印成下面格式的字符串,否则只会打印对象的内存地址
''' return f'{id}-{title}n{detail}n{url}'
这个类中只有两个魔术方法,一个是构造函数__init__,一个是__str__。
关于魔术函数,可以看这里:
如果没学过面向对象,可以在麦叔公众号:免费教程 -> 视频 小程序中搜“面向对象”,据说是B占最好的面向对象的教程。
我们要抓取的网址是这个:http://xiaohua.zol.com.cn/detail1/1.html 我们要抓的数据点有三个:
在谷歌浏览器中,右键点击检查,就可以在下面看到网页的代码结构:
通过分析这个结构,我们可以得出:成功这两个字是在一个h1结构内,这个h1的class是article-title,因为可以使用这个特征提取其中的内容(示例代码):
title = html.select_one('h1.article-title').getText()
用同样的方法可以分析出笑话内容和下一页URL的特征。
分析网页结构需要基本的HTML和CSS的知识,如果完全不懂,可以先直接模仿我的代码,然后再慢慢理解相关知识。
现在来看完整的代码。
新建一个名为joke_crawler.py的文件。
import requests import bs4 import time import random #先注释掉数据库相关的代码,
后面需要反注释回来 #import joke_db from joke import Joke #起始URL url = 'http://xiaohua.zol.com.cn/detail1/1.html'
#网站的域名地址,用来拼接完整地址 host = 'http://xiaohua.zol.com.cn' def craw_joke(url): '''
抓取指定的URL,返回一个Joke对象,和下一个要抓取的URL
如果抓取失败,返回None, None
必须设置User-Agent header,否则容易被封
''' print(f'正在抓取:{url}')
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/87.0.4280.88 Safari/537.36' }
html = requests.get(url, headers=headers).text
soup = bs4.BeautifulSoup(html, 'lxml')
try:
#分别使用css选择器提取title, detail和next_url title = soup.select_one('h1.article-title').getText()
detail = soup.select_one('div.article-text').getText().strip()
next_url = soup.select_one('span.next > a')['href']
return Joke(title, detail, url), next_url
except Exception as e:
print('出错了:', e)
print(html)
return None, None # 抓取笑话,以学习为目的,建议不要抓取太多,本例子只抓取了10个 count = 0 for i in range(0, 10):
joke, next_url = craw_joke(url)
if joke:
#先注释掉数据库相关的代码,后面需要反注释回来 #joke_db.save(joke) print(joke)
url = host + next_url
print('歇一会儿再抓!')
time.sleep(random.randint(1, 5))
print('抓完收工!')
代码中已经添加了一些注释,有基础的应该可以看懂。如果是零基础,可以在麦叔公众号:免费教程 -> 视频 小程序中搜“面向对象”,据说是B占最好的Python入门教程。
有两个点要注意:
抓来的笑话可以保存到文件中,但是用文件存储不方便检索,也不方便判断笑话是否重复等。
所以更好的方法是把笑话保存到数据库,这里选择sqlite做数据库。原因如下:
但如果你想把世界上所有的笑话都抓下来,数据量很大,那建议使用更正式的数据库,比如MySQL.
新建一个名为joke_db.py的文件
代码如下:
import sqlite3 from joke import Joke def setup(): '''
创建数据库和创建表,如果已经存在了不会重复创建
''' con = sqlite3.connect('jokeDB.db')
with con:
con.execute('''CREATE TABLE IF NOT EXISTS jokes
(id INTEGER PRIMARY KEY,
title varchar(256) NOT NULL,
detail varchar(1024) NOT NULL,
url varchar(1024) NOT NULL)''') def save(joke): '''
把笑话保存到数据库
根据url判断是否已经有这个笑话了,如果有了就不再保存
''' con = sqlite3.connect('jokeDB.db')
with con:
cur = con.cursor()
cur.execute(
'SELECT * FROM jokes WHERE (url = ?)', [(joke.url)])
has_joke = cur.fetchone()
if has_joke:
print('重复了,不再插入')
else:
con.execute('INSERT INTO jokes(title, detail, url) VALUES (?,?,?)',
(joke.title, joke.detail, joke.url))
print('笑话保存成功') def get_jokes(): '''
返回所有的笑话列表
''' print('loading jokes...')
con = sqlite3.connect('jokeDB.db')
jokes = []
with con:
for row in con.execute('SELECT * FROM jokes'):
joke = Joke(row[1], row[2], row[3], row[0])
jokes.append(joke)
return jokes # 调用最上面的代码 setup() # 测试代码,本模块被别的模块引入的时候,
不会执行下面的代码 if __name__ == '__main__':
save(Joke('笑话Test', '笑话内容test', 'https://www.joke.com/1.html'))
save(Joke('笑话Test2', '笑话内容test', 'https://www.joke.com/2.html'))
print('========打印一下所有的笑话======')
for joke in get_jokes():
print(joke)
print()
代码已经添加了比较多的注释,请先看代码。这里额外的补充:
运行上面的代码,就可以发现文件夹下多了一个名为jokeDB.db的文件,这是程序自动创建的数据库文件,笑话就保存在里面。下面里面只有两个测试的笑话:
> python joke_db.py
笑话保存成功
笑话保存成功
========打印一下所有的笑话======
loading jokes...
1-笑话Test
笑话内容test https://www.joke.com/1.html
2-笑话Test2
笑话内容test https://www.joke.com/2.html
这一部分需要一定的数据库知识,不过你也可以比这葫芦画瓢,先把功能做出来,再加强相关知识。
可惜麦叔还没有数据库相关的视频,如果你希望我出相关视频,请点赞和转发本文。
现在回到joke_crawler.py中,去掉关于joke_db的注释代码
第1处在文件开头:
#先注释掉数据库相关的代码,后面需要反注释回来 #import joke_db
第2处在文件的最下面:
for i in range(0, 10):
joke, next_url = craw_joke(url)
if joke:
#先注释掉数据库相关的代码,后面需要反注释回来 #joke_db.save(joke) print(joke)
url = host + next_url
print('歇一会儿再抓!')
time.sleep(random.randint(1, 5)) print('抓完收工!')
去掉注释后,再次运行joke_crawler.py,就会把笑话保存在数据库中。
为了验证是否保存成功了,可以去运行joke_db.py,因为这个文件最后会打印出所有的笑话:
========打印一下所有的笑话======
loading jokes... 1-笑话Test
笑话内容test
https://www.joke.com/1.html 2-笑话Test2
笑话内容test
https://www.joke.com/2.html 3-成功
她:“因为别人都不同情你,我才做你的妻子。”他:“你总算成功了。现在每个人都因此同情我。”
http://xiaohua.zol.com.cn/detail1/1.html 4-结婚以后
女:“为什么从前你对我百依百顺,可结婚才三天,你就跟我吵了两天的架?”男:“因为我的忍耐是有限度的。”
http://xiaohua.zol.com.cn/detail1/2.html 5-我们的
燕尔新婚,新娘对新郎说:“今后咱们不兴说‘我的’了,要说‘我们的’。”新郎去洗澡,良久不出,新娘问:“
你在干什么哪?”“亲爱的,我在刮我们的胡子呢。”
http://xiaohua.zol.com.cn/detail1/3.html 6-杞人忧天
妻子患了重病,医生宣告回天乏术。妻子即对丈夫说:“我现在希望你能够发誓。”“发什么誓。”“如果你再婚,
不准把我的衣服给你的新妻子穿。”丈夫恍然大悟道:“这个我可以发誓。说实话,你根本不必操心,
因为我再也不想找像你这样胖的太太了。”
http://xiaohua.zol.com.cn/detail1/5.html 7-理由充分
法官:“离婚理由是什么?”新娘:“他打呼噜。”法官:“结婚多长时间了?”新娘:“三天。”法官:
“离婚理由充分,结婚三天还不是打呼噜的时候。”
http://xiaohua.zol.com.cn/detail1/6.html 8-聪明丈夫
某夫妇当街而过,一只鸽子飞过天空,一泡鸽粪不偏不倚正巧落在太太肩上,太太急了,忙叫丈夫拿纸。丈夫抬头,
见鸽子不讲卫生,到处拉屎,却不知妻子叫他拿纸干嘛,说:“叫我有啥办法,追上前去给它擦屁股呀! ”
http://xiaohua.zol.com.cn/detail1/8.html 9-事故与灾难
一位夫人问她的丈夫:“亲爱的,你能告诉我‘事故’与‘灾难’这两个词之间有什么区别吗?”“这很简单。
”丈夫认真地回答说,“譬如你失足落水,这就叫‘事故’;如果人家又把你当鱼钓上来,这就是‘灾难’了。”
http://xiaohua.zol.com.cn/detail1/13.html 10-吵架的结果
夫妻吵架了。当丈夫下班回到家里,他发现妻子不在家。只在桌上留了一个条子,上面写道:“午饭在《烹调大全》第215页;晚饭在317页。”
http://xiaohua.zol.com.cn/detail1/14.html 11-保险之险
太太不懂保险的道理,认为缴保险费是浪费,先生连忙解释说:“保险是为了你和孩子,万一我死了;你们也有个保障呀!
”太太反驳说:“要是你不死呢?”
http://xiaohua.zol.com.cn/detail1/16.html 12-补不足
妻:“我晓得,你与我结婚,是因为我有钱。”夫:“不是,是因为我没有钱。”
http://xiaohua.zol.com.cn/detail1/17.html
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
在统计学与数据分析研究中,相关性分析分为数值变量分析与分类变量分析两大类。针对身高、销量、分数等连续数值变量,通常采用皮 ...
2026-10-12在Power BI可视化分析体系中,常规表格仅能展示枯燥的数值数据,存在数据对比不直观、强弱规律不突出、维度交叉分析低效等问题。 ...
2026-10-12 很多数据分析师能熟练写SQL、做透视表,但当被问到“数据是从哪里来的?经过哪些加工才进入数据仓库?ETL具体做了什么?”时 ...
2026-10-12在传统零售行业同质化竞争激烈、大众营销转化率持续走低的背景下,依托经验与直觉的粗放式营销逐渐失效。美国塔吉特百货(Target ...
2026-10-10随着智能客服、数字化服务、用户精细化运营的快速普及,传统零散、非标准化的客户服务数据与业务体系逐渐难以支撑高效服务治理。 ...
2026-10-10 很多数据分析师能熟练地写SQL、做透视表、算描述性统计,但当被问到“如何预测用户流失概率”“如何归因销量下滑的关键因素 ...
2026-10-10CDA数据分析师 出品 作者:李诗怡 定义区别 · 场景举例 · 指标分类 · 计算方法 知识体系总览 模块 包含内容 一、核 ...
2026-10-09在数据分析工作流中,业务数据大多存储在各类关系型数据库内,例如MySQL、PostgreSQL、SQLite等。Pandas是Python生态中主流的数 ...
2026-10-09在数字化精细化运营时代,海量用户存在需求差异、行为差异、价值差异与偏好差异。如果企业采用“一刀切”的统一营销、统一服务、 ...
2026-10-09 很多数据分析师拿到数据就开始清洗、建模,但当被问到“这批数据属于什么类型——结构化还是非结构化?分类变量还是数值变量 ...
2026-10-09指标体系是企业数字化分析、业务监控、经营决策的核心基础框架,是将零散数据转化为可衡量、可对比、可落地业务价值的关键体系。 ...
2026-10-08随着市场竞争日趋饱和,同质化低价竞争逐渐陷入内卷僵局,传统以价格、渠道、促销为核心的营销模式边际效益持续递减。在此背景下 ...
2026-10-08 很多数据分析师画过趋势图、做过业绩预测,但当被问到“这个月销售额增长20%,到底是长期趋势自然增长,还是促销活动的短期 ...
2026-10-08你有没有想过,手机里点外卖、刷社交软件、转一笔账,背后到底是谁在替你"记着账"? 答案其实很简单:数据库,以及跟它对话的那 ...
2026-10-07CDA数据分析师 出品 作者:李诗怡 一、数据分析四大思维 1. 对比思维:没有对比就没有分析 核心观点:单独一个数字没有意义,有 ...
2026-10-05Kimball 是方法,星型模型是它产出的形状。 很多人把"Kimball vs 星型模型"当成一道选择题——这本身就是个误会:Kimball 是动词 ...
2026-10-05写在开头 老板在微信上甩来一句: "帮我看下为什么销量跌了。" ” 你回工位,打开 SQL,开始写。查订单表、拉近三个月、按 ...
2026-10-03CDA数据分析师 出品 作者:李诗怡 1. 事实表 vs 维度表 对比维度 事实表 维度表 核心问题 记录“业务发生了什么事” 描述 ...
2026-10-02做数据聚合时,PySpark的groupBy()确实能完成统计,这也是它的本职工作。但它有一个根本性局限:每一组数据,最终只能返回一行 ...
2026-10-01热力地图是数据可视化中极具辨识度与实用性的空间分析图表,结合地理空间维度与数据密度特征,通过颜色深浅、色阶渐变直观展示数 ...
2026-09-30