前言:
五一假期的时候,对沪漂小窝进行了一下升级,把文本分类器的实现改成了自主实现,看着很深奥,其实很简单,我在群里分享后,有个小伙伴说,有一些信息是重复的,当时我说这里仅仅实现了按照id去重,并没有按照语义去重。

优化思路:
当时我是想着如果全数据库搜索,挨个排查,会很费劲。其实我忽略了一点,想这种标题相似度很高的帖子其实都是同一个创建者(同一个id)发布的,换句话说只需要将最近一个月内当前创建id的在数据库的帖子查询出来,遍历一遍,如果其中一个跟待入库的信息相似度很高,那么就可以认为是重度的帖子,对于系统筛选来说是无意义的内容,则不入库。
那么这样把内容理出来,实现就比较简单了。在可操作性上和时效性上,并没有很大的阻碍。
具体实现:
那么如何进行相似度区分呢,这个很简单,在之前使用分类算法的时候,计算的是段落的TF-IDF值,那么两个TF-IDF的向量很接近,那么就可以认为这两句话是表达同一个内容。关键点就在于这个阈值怎么界定。
先用个例子来演示一下,比较两个句子之前的相似性。
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from scipy.linalg import norm
def tfidf_similarity(s1, s2):
# 文字拆分
s1, s2 = ' '.join(list(s1)), ' '.join(list(s2))
# 转化为TF矩阵
cv = TfidfVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 计算TF系数
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
if __name__ == '__main__':
new = '全上海地铁沿线 整租公寓 小区整租合租 押一付一无中介费。拎包入住 '
s1 = '中介勿扰,个人实帖直租12号线虹漕路地铁口,华悦家园独立一室户3400,可押1付1,紧邻漕河泾田林上师大。'
s2 = '13号线金运路地铁口,大宅风范城,精装朝南一室户燃气厨房,家电全配,拎包入住,押一付一'
s3 = '全上海地铁沿线 整租公寓 精装小区房,整租一室户。押一付一 无中介费'
print(tfidf_similarity(s1, new))
print(tfidf_similarity(s2, new))
print(tfidf_similarity(s3, new))
代码运行结果:
0.15741852956377606
0.18237168346404925
0.7414888327522697上文的例子表示,假设现在数据库中已经保存了A发布(时间选择最近一个月)的3条帖子,分别是s1,s2,s3。现在有一条A新发布的帖子的内容(标题)为new。现在将new分别与3条信息比对,发现中new和s3的TF系数比较高。比较两个文本,可以发现,这两个内容表达的意思基本是一样的。
那么由此断定new这条数据是无效数据或者说用户重复发布的信息,不应该是进入到信息筛选表中,所以直接pass。
以前玩豆瓣(发沪漂小窝的广告)的时候,可能有这么一个限制是短时间内不能发布同样的信息,所以就会把相同内容的帖子,修改一下名字,加一些空格、特殊字符(比如~,!!等等),已达到多次发布的效果。这样增加可能被别人看到的概率。
代码升级:
#更新 MyscrapyPipeline 方法
class MyscrapyPipeline:
def process_item(self, item, spider):
result = [item["title"], item["createDate"], item["text"], item["crawDate"], item["url"], item["creator"]]
haveOne = [item["creator"]]
image_urls = item["image_urls"]
# 查询同一个创建者最近一个月发布的帖子
querySql = "select title from house_info where creator=(%s) and crawDate >= DATE_SUB(NOW(),INTERVAL 30 day)"
houses = dbUtil.get_all(querySql, haveOne)
# 如果存在相同的帖子则不保存
if rentClassify.check(houses, item["title"]):
saveSql = "insert into house_info (title, createDate, text,crawDate,url,creator) values (%s,%s,%s,%s,%s,%s)"
h_id = dbUtil.save(saveSql, result)
# 分析租房信息的分类
info = rentClassify.analysis(item["url"], item["creator"], item["title"] + item["text"])
# 添加租房信息的id
info.append(h_id)
infoSaveSql = "insert into rent_info (url, station, `identity`,price,pay,only_girl,rent_type,`count`,create_date,h_id) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
# 保存信息分类
r_id = dbUtil.save(infoSaveSql, info)
# 图片链接保存
if len(image_urls) > 0:
imageSql = "INSERT INTO image(r_id,url) VALUES (%s,%s)"
for image_url in image_urls:
i = [r_id, image_url]
dbUtil.save(imageSql, i)
return item
全部完整代码已经push到我的github中,感兴趣的可以pull或者clone
地址:https://github.com/mlscoder/hupiao
PS:
小程序移动端和服务端也做了相应的改动,主要是表字段和结构的更新,最新版本的代码更新在github上。原来gitee的内容是老版本的内容,与现在的版本的不兼容。有需要的可以去这里获取。

我是马拉松程序员,可不止于代码!