项目介绍

为什么会有这么”奇葩“的项目呢?

这个想法的出现来源于,我之前想过要写一篇小说,但是苦于思考创造人名这一步,所以就在想能不能有一个网站,我只需要输入姓氏,就能得到一些人名?所以就有了这个项目。

哪里来的数据呢?

这个问题,其实也不难解决,百度搜一会就有答案啦。于是我找到了姓名大全这个网站,右击给这个网站做了一个“检查”,发现这个网站的构造挺简单的,不需要太高深的爬虫技巧就可以获取到数据。再把数据存到sqlite3数据库中,作为这个项目的源数据。

这个项目会很复杂吗?

越学越简单,越简单越难。

任何项目都可以很复杂的,所以以我的能力,尽可能的去实现较“复杂”的操作。

其实,乍一看,这个项目不复杂:

  1. 爬虫获取数据,并保存到数据库。
  2. 后端从数据获取数据,发送到前端。
  3. 前端一个搜索框,根据输入的姓氏,从后端得到姓名并展示。

我在这个项目中主要用到的是这些技术:requests,flask,vue。有些知识我也不是很懂,都是直接一边学一边用,我觉得学以致用,更能感受到学习的乐趣。

源代码获取

公众号“帅帅的Python”,回复“姓名”即可获取源码。

数据获取

网站分析

需要从姓名大全这个网站获取的数据有:

  1. 百家姓列表:[‘赵’,‘钱’,‘孙’,‘李’,…]
  2. 每个姓对应的男生名、女生名

获取一个百家姓列表,主要是为了判断前端输入的姓氏是否正确。

一、在谷歌浏览器中,右击“检查”,通过一番分析后,找到一个a标签,如下形式,可以通过这个标签,获取到该姓氏的具体的超链接(通过href获取),以及百家姓列表(通过文本拆分获取)。

<a class="btn btn2" href="//zhao.resgain.net/name_list.html" title="赵姓名字大全共有赵姓名字162998个">赵姓名字大全</a>

二、继续分析,点击超链接进入到姓氏的详情页,发现每个姓氏都有男生和女生的超链接,如下:

# 男生网站为:http://zhao.resgain.net/name/boys.html
# 女生网站为:http://zhao.resgain.net/name/girls.html

与之前分析的a标签中的href中的超链接有所不同,所以需要将**/name_list.html替换为/name/boys.html/name/girls.html**才能得到对应姓氏的男生、女生姓名地址。

三、进入男生姓名网站中,每个姓名的html代码如下:

<a href="#" class="livemsg" data-name="赵竹林"><span class="glyphicon glyphicon-comment"></span></a>

通过class="livemsg"定位到该a标签,然后获取到data-name的属性值即可得到姓名。

数据库设计

这里选择sqlite3为数据库,主要是因为不需要复杂的配置,操作简单,轻便小巧。

这里只需要存姓名(NAME)、性别(SEX)两个字段即可,当然,再加一个自增的ID字段。

数据库设计代码如下:

def create_db():conn = sqlite3.connect(r"tools_app.db")cursor = conn.cursor()cursor.execute('''CREATE TABLE RANDOM_NAME(ID INTEGER PRIMARY KEY  AUTOINCREMENT,NAME           TEXT    NOT NULL,SEX         TEXT    NOT NULL);''')print("RANDOM_NAME created successfully")conn.commit()conn.close()

代码解释:

1、conn是一个sqlite3数据库对象,只需传入database参数,这里传的tools_app.db表示,连接的是同级目录下的名为tools_app.db的数据库(数据库不需要提前创建)

2、cursor为操作数据库的“游标”,可以把数据库比喻成一个仓库,游标就像是一个优秀的员工,cursor.execute()就是这个员工需要对这个仓库做的事情;conn.commit()就是员工操作完后,仓库将操作的结果告诉员工。conn.close()仓库关门,期待下一次的开启。

这个函数运行完之后,会在同级目录下创建一个名为tools_app.db的数据库,该数据库中有一个名为RANDON_NAME的表。

爬虫设计

通过之前的网站分析,爬虫的运行应该分为以下三步:

  1. 通过姓名大全地址获取到每个姓氏href和文本信息。
  2. 通过第一步获取的href,获取男生和女生的网站。
  3. 通过第二步获取的网站获取对应的姓名。

第一步,代码详解:

def get_name_link():url = "http://www.resgain.net/xmdq.html"res = requests.get(url)soup = BeautifulSoup(res.text, 'lxml')name_links = []for s in soup.find_all(attrs={'class': 'btn btn2'}):name_links.append("http:" + s.get('href'))return name_links

代码解释:

1、通过request.get()方法,传入url就可以获取到对应的网页对象,使用res.text属性获取到网页源码

2、使用BeautifulSoup格式化网页,可以很方便,很快速的定位到需要的数据。

3、soup.find_all(attrs={‘class’: ‘btn btn2’}),通过class属性为btn btn2找到所有符合条件的元素,返回的是一个列表

4、通过s.get(‘href’)方法可以获取到标签元素的href属性的值

同理,可以获取到百家姓列表:

def get_name_list():url = "http://www.resgain.net/xmdq.html"res = requests.get(url)soup = BeautifulSoup(res.text, 'lxml')name_list = []for s in soup.find_all(attrs={'class': 'btn btn2'}):name_list.append(s.text.split("姓")[0])return name_list

1、s.text可以获取该标签中的文本内容

2、split(“姓”)方法,按照"姓"分隔字符串,结果为一个列表,选择第一个即为百家姓的姓氏。例如:"赵姓名字大全"分隔后变成:[“赵”,“姓名字大全”]

第二步,构造男生和女生的详情链接:

name_link_list = get_name_link()
for name_link in name_link_list:url_boys = name_link.replace("/name_list.html", "/name/boys.html")url_girls = name_link.replace("/name_list.html", "/name/girls.html")

1、字符串替换用replace(需要替换的内容,替换的内容)

第三步,通过构造的链接获取姓名:

def get_data(url):con = sqlite3.connect(r'tools_app.db')cursor = con.cursor()res = requests.get(url)soup = BeautifulSoup(res.text, 'lxml')if "boy" in url:sex_ = "男"else:sex_ = "女"for s in soup.find_all(attrs={'class': 'btn btn-default btn-lg namelist'}):name = s.find(attrs={'class': 'livemsg'}).get('data-name')sql = "insert into RANDOM_NAME (name,sex) values('{0}','{1}');".format(name,sex_)cursor.execute(sql)con.commit()print(url, "完成")con.close()

1、同样连接tools_app.db数据库,创建一个cursor对象,用来操作数据库

2、同样,使用soup.find_all()获取到所有的标签,通过get方法获取到姓名数据

3、构造sql语句,用format()方法格式化字符串

4、cursor.execute()执行sql插入语句,conn.commit()提交记录,最后for循环结束后,conn.close()关闭连接

开始运行:

if __name__ == '__main__':# 创建数据库和RANDOM_NAME表# print(get_name_list())# create_db()# get_data('http://zhang.resgain.net/name/boys.html')name_link_list = get_name_link()for name_link in name_link_list:url_boys = name_link.replace("/name_list.html", "/name/boys.html")url_girls = name_link.replace("/name_list.html", "/name/girls.html")get_data(url_boys)t = random.randint(1, 3)time.sleep(t)get_data(url_girls)t = random.randint(1, 3)time.sleep(t)

爬虫获取数据总计耗时2小时左右,获取到数据249298条。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RO6i5PpF-1617715189324)(./pictures/random_names/爬虫数据展示.png)]

前端设计

采用Bootstrap4前端框架、vue2.js框架、vue-ajax-axios异步加载框架。

页面构思

  1. 需要一个简单的搜索框,还需要两个列表组来展示搜索的男生和女生姓名。
  2. 第一次进入,会有默认的页面,默认加载郑姓的数据。
  3. 在前端这里判断输入的姓氏,是否在百家姓里面,如果不在,将确定按钮改为红色“禁用”。
  4. 输入正确的姓氏后,每次点击确定按钮,展示的姓名都是随机的。

确定页面展示:

禁用页面展示:

代码分析

卡片

Bootstrap4-卡片

将页面卡片化,会感觉更加的清晰明了,只需要将输入框放到card-header,列表组放到card-body中即可。

<div class="card"><div class="card-header">Featured</div><div class="card-body"><h5 class="card-title">Special title treatment</h5><p class="card-text">With supporting text below as a natural lead-in to additional content.</p><a href="#" class="btn btn-primary">Go somewhere</a></div>
</div>

输入框

Bootstrap4-输入组地址

这里有许多漂亮的输入框,选择一个简单的即可。

<div class="input-group mb-3"><input type="text" class="form-control" placeholder="Recipient's username" aria-label="Recipient's username" aria-describedby="button-addon2"><div class="input-group-append"><button class="btn btn-outline-secondary" type="button" id="button-addon2">Button</button></div>
</div>

当然,直接拿来用是不行的,需要修改一番。使用vue框架的v-if来判断输入的姓氏是否正确,用v-on:click来触发点击事件。

1、判断一个元素在不在一个数组中,js的判断方法为:indexOf(),结果为-1表示不在数组中。

2、v-if和v-else所在的标签也紧挨着,才能生效。

修改代码如下:

<div class="input-group"><input type="text" class="form-control" v-model="input_name" placeholder="您的姓氏"><div class="input-group-append"><button class="btn btn-primary" v-if="name_index >= 0" v-on:click="get_info">确定</button><button class="btn btn-danger" v-else>错误</button></div>
</div>

列表组

Bootstrap4-列表组地址

这里涉及到男生和女生,所以选择将两个列表组,并排放置在同一行中。

<div class="row"><div class="col-sm-6"><ul class="list-group"><button type="button" class="list-group-item list-group-item-action active"aria-current="true"><b>男生姓名:</b></button><button class="list-group-item list-group-item-action text-center"v-for="name in names['boy_names']">{[ name ]}</button></ul></div><div class="col-sm-6"><ul class="list-group"><button type="button" class="list-group-item list-group-item-action active"aria-current="true"><b>女生姓名:</b></button><button class="list-group-item list-group-item-action text-center"v-for="name in names['girl_names']">{[ name ]}</button></ul></div>
</div>

JavaScript

使用vue的语法

1、由于vue的模板是{{}},与flask中内置的jinja2语法有冲突,所以需要用delimiters参数将vue的模板改为[{}]。

2、data:input_name为默认的姓氏;names中包含默认的男生和女生姓名;right_first_name为正确的百家姓氏。

3、name_index:默认为1,表示input_name在right_first_name中。

4、watch:监听input_name的值是否改变,函数的nval参数为新输入的姓氏。

5、methods:用ajax从后端的get_names路由获取对应的数据。

vm = new Vue({el: "#rn_app",delimiters: ['{[', ']}'],data: function () {return {"input_name": "郑","names": {"boy_names": ['郑宇坤', '郑鑫黎', '郑闰', '郑文锶', '郑小垚'],"girl_names": ['郑琪', '郑雯欣', '郑丽萍', '郑伊春', '郑蘅'],},"right_first_name": ['赵', '钱', '孙', '李', '周', '吴', '郑', '王',......],"name_index": 1}},watch: {input_name: function (nval) {this.name_index = this.right_first_name.indexOf(nval);}},methods: {get_info: function () {axios.get("/get_names", {params: {"input_name": this.input_name}}).then(response => (this.names = response.data.names)).catch(function (error) {console.log(error)})}}})

后端

根据前端的分析,后端需要2个接口,index和get_names接口,index负责展示页面,get_names返回查询的姓名。

index接口

@app.route("/")
def index():return render_template("random_name.html")

get_names接口

1、数据库配置为当前路径下的/spiders/tools_app.db。

2、使用SQL语句随机获取数据。

3、用jsonify将数据封装为json数返回。

@app.route("/get_names", methods=["GET", "POST"])
def get_names():con = sqlite3.connect(r"./spiders/tools_app.db")cursor = con.cursor()input_name = request.args.get("input_name", "None")# 根据input_name从数据库随机获取5个男生姓名,和5个女生姓名boy_sql = f"select name from RANDOM_NAME where NAME like '{input_name}%' and SEX='男' order by random()limit 5"girl_sql = f"select name from RANDOM_NAME where NAME like '{input_name}%' and SEX='女' order by random()limit 5"cursor.execute(boy_sql)con.commit()boy_names_list = [i[0] for i in cursor.fetchall()]cursor.execute(girl_sql)con.commit()girl_names_list = [i[0] for i in cursor.fetchall()]con.close()return jsonify({"names": {"boy_names": boy_names_list, "girl_names": girl_names_list}})

启动

本地启动,端口为5000端口,启动后访问http://localhost:5000/,即可看到页面。

if __name__ == '__main__':app.run(host="localhost", port=5000, debug=True)

源代码获取

公众号“帅帅的Python”,回复“姓名”即可获取源码。

根据输入的姓氏,随机获取对应的男生和女生姓名相关推荐

  1. 微信小程序云数据库触底分页加载,下拉无限加载,第一次请求数据随机,随机获取数据库的数据

    效果图 小程序云开发分页加载代码 <!--pages/chatList/chatList.wxml--> <view class="pageTitle">家 ...

  2. python输入时间_【转】python 输入一个时间,获取这个时间的下一秒

    输入一个时间,获取这个时间的下一秒 PS:下面代码使用于 python 2.7 time1 = raw_input("输入一个时间[HH:MM:SS]:") time1List = ...

  3. mysql每组随机一条_MySql分组后随机获取每组一条数据的操作

    思路:先随机排序然后再分组就好了. 1.创建表: CREATE TABLE `xdx_test` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAUL ...

  4. mysql高效率写法_mysql高效率随机获取n条数据写法

    今天做项目遇到这个问题,本来想用mysql自带的随机函数来实现,但是想到这样做功能是实现了,但是效率真的好差!一下子想不到好的方法,就去网上找了一下,记录下来,好好研究学习一下. ID连续的情况下(注 ...

  5. 使用 SQL 语句从数据库一个表中随机获取一些数据

    以前从一个表中随机获取数据的时候,都是先把数据读取到来, 然后再在程序中来随机抽取一部分数据, 今天告诉大家一种使用 SQL 语句的方法来随机获取一部分数据 SQL Server: -- 随机获取 1 ...

  6. qtp web随机获取下拉框的值

    1.先顶一个随机获取数的函数 Function GetList(i)    randomize    GetList=RandomNumber(0,i) End Function 2.以下就是纯代码了 ...

  7. Hibernate随机获取指定范围内的指定条目的记录

    下面这段代码是我最近写项目用到的,功能是查找用户列表 限制条件是:指定用户周边.条数限制.随机获取.指定范围 public List<User> listUserByLocation(St ...

  8. mysql 随机分组_MySql分组后随机获取每组一条数据的操作

    MySql分组后随机获取每组一条数据的操作,数据,效率,时间,李四,条数 MySql分组后随机获取每组一条数据的操作 易采站长站,站长之家为您整理了MySql分组后随机获取每组一条数据的操作的相关内容 ...

  9. Oracle 随机获取N条数据

    Oracle 随机获取N条数据     当我们获取数据时,可能会有这样的需求,即每次从表中获取数据时,是随机获取一定的记录,而不是每次都获取一样的数据,这时我们可以采取Oracle内部一些函数,来达到 ...

  10. java map 随机取值_随机获取一个集合(List, Set)中的元素,随机获取一个Map中的key或value...

    利用Java提供的Random类.从List或Set中随机取出一个元素,从Map中随机获取一个key或value. 因为Set没有提供get(int index)方法,仅仅能先获取一个随机数后.利用一 ...

最新文章

  1. 安全专家十年磨一剑的独门秘笈,威力堪比《九阴真经》
  2. 维基链超级节点竞选的具体细节有哪些?
  3. PetShop4,错误提示:System.Web.Security.SqlMembershipProvider”要求一个与架构版本“1”兼容的数据...
  4. transient关键字的作用_ArrayList Vector (transient关键字)--JAVA成长之路
  5. 程序员,想搞事情?进来,就一句话的事!
  6. Science | 从结构生物学的角度理解人类mRNA剪接体分支位点的识别
  7. Linux学习笔记-标准库中的管道操作
  8. 从四个问题透析Linux下C++编译链接
  9. 2021年广东省高考成绩查询入口,广东省教育考试院:2021年广东高考成绩查询入口、查分系统...
  10. java 解析数据包_java - 如何在Java中正确解析TCP数据包? - 堆栈内存溢出
  11. 直接访问WEB-INF目录下的JSP页面的方法
  12. 【算法工程师面试题40】基于逻辑回归算法实现电影推荐
  13. 手机版wps怎么制作折线图_怎么制作手机铃声 录制声音片段方法
  14. 计算机专业就业方向与前景以及你所要具备的技能(本科生)
  15. axure中出现小手_Axure高保真滑动拼图解锁实例教程
  16. nginx: worker process is shutting down
  17. TreeGrid插件简练了解使用
  18. 获取当前时间的毫秒时间戳
  19. 又一家安防公司即将登录创业板-睿联Reolink
  20. download下载图片

热门文章

  1. arctanx麦克劳林公式推导过程_【数学】「专题」初识泰勒级数(Taylor Series)与泰勒公式(Taylor#x27;s Formula)...
  2. RabbitMQ Federation 插件使用
  3. PostgreSQL模糊查询
  4. matlab中列主元三角分解法的函数,[数值算法]列主元三角分解法
  5. mysql recordcount_[已解决]为什么RecordCount的值为-1??
  6. Python: PS 图像特效 — 模糊玻璃
  7. 小米蓝牙耳机airdots青春版双耳模式
  8. unity shader development[11]
  9. 多边形的定义为什么要强调封闭图形_11.3.1 多边形讲解.ppt
  10. tooltips使用教程(鼠标悬停时显示提示)