查看原文
其他

Python之由公司名推算出公司官网(余弦相似度)

howie6879 老胡的储物柜 2022-06-01

读大学时期写的博文

1.问题

对展会数据分类后,我的新任务是如何通过 公司名、公司地址、国家等海关数据推断出该公司的官网网站(若官网不存在则不考虑) 以下数据仅供参考:

公司名国家地址
JPW INDUSTRIES INC
427 NEW SANFORD RD LAVERGNE TN 37086 US
Fujian Xishi Co., LtdCN, CHINA
BusinessPartner Co.,ltd

BENKAI Co.,Ltd

GOLD INC
18245 E 40TH AVE AURORA CO 80011 US

需要得到结果:

公司名官方网站
JPW INDUSTRIES INChttp://http://www.jpwindustries.com/
Fujian Xishi Co., Ltdhttp://www.xishigroup.com/
BusinessPartner Co.,ltdhttp://www.traderthailand.com/
BENKAI Co.,Ltdhttp://www.benkaico.com
GOLD INChttps://goldbuginc.com/

2.解决

由数据可看出,公司名是绝对存在的,故解决思路是从公司名出发,而不怎么全面的国家以及地址信息则用来提高准确度。

大体思路是这样的,若公司官网存在,那么通过搜索引擎定会被检索到,搜索引擎自然首选 google,所以可以先通过获取谷歌搜索的结果,然后分析获取的结果,从而得出最可能是该公司网站的 url。 初步搜索一下,看看各种情况:

  • 第一种情况,检索即可很直观地得出结果

  • 第二种情况,检索不能直观地得出结果,但官网确实存在(第二检索个结果)

  • 第三种情况,输入公司名+公司地址和只输入公司名得出的结果不一样

对于第三种情况,可以看出输入公司名+公司地址得出的结果是绝对正确的。

观察第三种情况,当输入公司名+公司地址时,返回结果的右侧会出现公司的详细信息,经过验证,若出现这种情况,则其 website对应的 url绝对正确。

故代码的第一步骤可以首先检索公司名+公司地址,观察 website元素是否存在,若存在,返回公司官网,否则,对公司名进行检索,代码如下:

  1. def searchWeb(query, tld='com', lang='en', tbs='0', num=10, safe='off', tpe='', user_agent=None):

  2.    query = quote_plus(query)

  3.    get_page(url_home % vars())

  4.    url = url_search_num % vars()

  5.    # Request the Google Search results page.

  6.    html = get_page(url)

  7.    try:

  8.        href = re.findall(r'Directions</a><a\s*class="fl"\s*href="(.*?)".*?>Website', str(html))[0]

  9.        link = filter_result(href)

  10.        return link

  11.    except:

  12.        pass

  13.    return None

能直接获取公司官网毕竟是少数,大多数据还是要通过一步步计算得出,主要经过以下步骤:

  • 获取搜索引擎检索结果提取url

  • 初步排除某些url

  • 余弦相似度计算最可能的结果

2.1.获取搜索引擎检索结果提取url

对于谷歌搜索,我使用了 MarioVilas的项目google,毕竟在国内,为了以防万一我也写了 yahoo搜索,代码如下:

  1. #!/usr/bin/env

  2. # -*-coding:utf-8-*-

  3. # script: yahooSearch.py

  4. __author__ = 'howie'


  5. import sys

  6. import time


  7. if sys.version_info[0] > 2:

  8.    from urllib.request import Request, urlopen

  9.    from urllib.parse import quote_plus, urlparse

  10. else:

  11.    from urllib import quote_plus

  12.    from urllib2 import Request, urlopen

  13.    from urlparse import urlparse, parse_qs


  14. try:

  15.    from bs4 import BeautifulSoup


  16.    is_bs4 = True

  17. except ImportError:

  18.    from BeautifulSoup import BeautifulSoup


  19.    is_bs4 = False


  20. url_search = "https://search.yahoo.com/search?p=%(query)s&b=%(start)s&pz=%(num)s"


  21. headers = {

  22.    'accept-encoding': 'gzip, deflate, sdch, br',

  23.    'accept-language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4',

  24.    'upgrade-insecure-requests': '1',

  25.    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',

  26.    'cache-control': 'max-age=0',

  27.    'authority': ' search.yahoo.com'

  28. }

  29. # 默认user_agent

  30. user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'



  31. def filter_link(link):

  32.    try:

  33.        linkData = urlparse(link)

  34.        if linkData.netloc and "yahoo.com" not in linkData.netloc and "/search/srpcache" not in linkData.path:

  35.            return link

  36.    except Exception:

  37.        pass

  38.    return None



  39. def yahooSearch(query, start=0, num=10, page=1, pause=1.0):

  40.    """

  41.    获取雅虎搜索url

  42.    :param query: 搜索关键词

  43.    :param start: 开始条目,最好为0

  44.    :param num: 搜索条目 建议10的倍数

  45.    :param page: 页数

  46.    :param pause: 停顿时间

  47.    :return: 返回url

  48.    """

  49.    query = quote_plus(query)

  50.    while page > 0:

  51.        url = url_search % vars()

  52.        time.sleep(pause)

  53.        request = Request(url)

  54.        request.add_header('User-Agent', user_agent)

  55.        response = urlopen(request)

  56.        html = response.read()

  57.        if is_bs4:

  58.            soup = BeautifulSoup(html, 'html.parser')

  59.        else:

  60.            soup = BeautifulSoup(html)

  61.        anchors = soup.find(id="web").find_all('a')

  62.        for a in anchors:

  63.            try:

  64.                link = filter_link(a["href"])

  65.                if link:

  66.                    yield link

  67.            except KeyError:

  68.                continue

  69.        start += num + 1

  70.        page -= 1



  71. if __name__ == '__main__':

  72.    # GOLD INC 18245 E 40TH AVE AURORA CO 80011 US

  73.    for url in yahooSearch("GOLD INC 18245 E 40TH AVE AURORA CO 80011 US"):

  74.        print(url)

2.2.初步排除某些url

这个可根据个人需求来配置,可添加 webConfig.py脚本,排除某些 url:

  1. # -*-coding:utf-8-*-

  2. __author__ = 'howie'


  3. config = dict(

  4.    # www开头或分割后数组大于二的网站

  5.    forbid_www=["www.linkedin.com", "www.alibaba.com"],

  6.    # 非www开头的网站

  7.    forbid=["imexbb.com", "made-in-china.com"]

  8. )

以公司名 BENKAICo.,Ltd为例,初步获取url: ['http://www.benkaico.com','http://hisupplier.com','http://www.hktdc.com/en']

2.3.余弦相似度计算最可能的结果

对于公司 BENKAICo.,Ltd,我们获得了三个结果,现在又该如何从该列表中取得最可能的结果呢。

这里可以采用余弦相似度,具体公式可google,稍稍解释下,对于这三个网站:

  1. ['http://www.benkaico.com', 'http://hisupplier.com', 'http://www.hktdc.com/en']```

  2. 可以通过计算各个网站的`title`和`BENKAI Co.,Ltd`的相似程度来取得最可能的结果。

  3. #####2.3.1:对各网站title进行分词

  4. ```python

  5. {'http://www.benkaico.com': ['benkai', 'co.', ',', 'ltd', '.'],

  6. 'http://www.hktdc.com/en': ['hktdc.com', 'â\x80\x93', 'page', 'not', 'found'],

  7. 'http://hisupplier.com': ['china', 'suppliers', ',', 'suppliers', 'directory', ',', 'china', 'manufacturers', 'directory', '-', 'hisupplier.com']}

2.3.2:构建单词向量

  1. {'http://www.benkaico.com': [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]],

  2. 'http://www.hktdc.com/en': [[1, 1, 0, 0, 1, 0, 0, 0, 1], [0, 0, 1, 1, 0, 1, 1, 1, 0]],

  3. 'http://hisupplier.com': [[0, 0, 0, 1, 0, 0, 1, 1, 0, 1], [2, 1, 1, 0, 2, 1, 2, 0, 2, 0]]}

2.3.3:计算余弦相似度

  1. {'http://www.benkaico.com': 0.94427190999915878,

  2. 'http://www.hktdc.com/en': 0.0,

  3. 'http://hisupplier.com': 0.31451985913875646}

通过比较,可以看到 http://www.benkaico.com相似度最高的结果,跟真实结果一样。

全部步骤代码如下:

  1. # -*-coding:utf-8-*-

  2. __author__ = 'howie'

  3. from urllib import parse

  4. from bs4 import BeautifulSoup

  5. from collections import defaultdict

  6. import requests

  7. import re

  8. import nltk

  9. import time

  10. from config.webConfig import config

  11. from CosineSimilarity import CosineSimilarity

  12. from search.yahooSearch import yahooSearch

  13. from search.gooSearch import search, searchWeb



  14. class Website(object):

  15.    """

  16.    通过公司名等信息获取网站官网

  17.    """


  18.    def __init__(self, engine='google'):

  19.        self.forbid_www = config["forbid_www"]

  20.        self.forbid = config["forbid"]

  21.        self.engine = engine


  22.    def get_web(self, query, address=""):

  23.        """

  24.        获取域名

  25.        :param query: 搜索词

  26.        :param address: 在此加上地址时,query最好是公司名

  27.        :return: 返回最可能是官网的网站

  28.        """

  29.        if self.engine == 'google' and address:

  30.            allQuery = query + " " + address

  31.            result = searchWeb(query=allQuery, num=5)

  32.            if result:

  33.                return result

  34.        allDomain = self.get_domain(query)

  35.        if len(allDomain) == "1":

  36.            website = allDomain[0]

  37.        else:

  38.            # 初步判断网站域名

  39.            counts = self.get_counts(allDomain)

  40.            largest = max(zip(counts.values(), counts.keys()))

  41.            if largest[0] > len(allDomain) / 2:

  42.                website = largest[1]

  43.            else:

  44.                # 获取对应域名标题

  45.                domainData = self.get_title(set(allDomain))

  46.                # 计算相似度

  47.                initQuery = nltk.word_tokenize(query.lower(), language='english')

  48.                # 余弦相似性计算相似度

  49.                cos = CosineSimilarity(initQuery, domainData)

  50.                wordVector = cos.create_vector()

  51.                resultDic = cos.calculate(wordVector)

  52.                website = cos.get_website(resultDic)

  53.        return website


  54.    def get_domain(self, query):

  55.        """

  56.         获取谷歌搜索后的域名

  57.        :param query:搜索条件

  58.        :return:域名列表

  59.        """

  60.        allDomain = []

  61.        if self.engine == "google":

  62.            for url in search(query, num=5, stop=1):

  63.                allDomain += self.parse_url(url)

  64.        elif self.engine == "yahoo":

  65.            for url in yahooSearch(query):

  66.                allDomain += self.parse_url(url)

  67.        if not allDomain:

  68.            allDomain.append('')

  69.        return allDomain


  70.    def parse_url(self, url):

  71.        allDomain = []

  72.        domainParse = parse.urlparse(url)

  73.        # 英文网站获取

  74.        if "en" in domainParse[2].lower().split('/'):

  75.            domain = domainParse[1] + "/en"

  76.        else:

  77.            domain = domainParse[1]

  78.        domainList = domain.split('.')

  79.        # 排除干扰网站

  80.        if len(domainList) >= 3 and domainList[0] != "www":

  81.            isUrl = ".".join(domain.split('.')[-2:])

  82.            if isUrl not in self.forbid:

  83.                allDomain.append(domainParse[0] + "://" + isUrl)

  84.        elif domain not in self.forbid_www:

  85.            allDomain.append(domainParse[0] + "://" + domain)

  86.        return allDomain


  87.    def get_title(self, setDomain):

  88.        """

  89.        获取对应网站title,并进行分词

  90.        :param allDomain: 网站集合

  91.        :return: 网站:title分词结果

  92.        """

  93.        domainData = {}

  94.        for domain in setDomain:

  95.            headers = {

  96.                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",

  97.                "Accept-Encoding": "gzip, deflate, sdch",

  98.                "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4",

  99.                "Cache-Control": "max-age=0",

  100.                "Proxy-Connection": "keep-alive",

  101.                "Upgrade-Insecure-Requests": "1",

  102.                "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",

  103.            }

  104.            try:

  105.                data = requests.get(domain, headers=headers).text

  106.                soup = BeautifulSoup(data, 'html.parser')

  107.                title = soup.title.get_text()

  108.                title = re.sub(r'\r\n', '', title.strip())

  109.                titleToken = nltk.word_tokenize(title.lower(), language='english')

  110.                domainData[domain] = titleToken

  111.            except:

  112.                pass

  113.        return domainData


  114.    def get_counts(self, allDomain):

  115.        """

  116.        返回网站列表各个域名数量

  117.        :param allDomain: 网站列表

  118.        :return: 网站:数量

  119.        """

  120.        counts = defaultdict(int)

  121.        for eachDomain in allDomain:

  122.            counts[eachDomain] += 1

  123.        return counts



  124. if __name__ == '__main__':

  125.    # allQuery = ["National Sales Company, Inc.", "Decor Music Inc.","Fujian Xishi Co., Ltd","Kiho USA Inc.","BusinessPartner Co.,ltd","BENKAI Co.,Ltd"]

  126.    # GOLD INC 18245 E 40TH AVE AURORA CO 80011 US

  127.    allQuery = ["ALZARKI INTERNATIONAL"]

  128.    website = Website(engine='google')

  129.    for query in allQuery:

  130.        time.sleep(2)

  131.        website = website.get_web(query=query)

  132.        print(website)

计算余弦相似度代码

  1. # -*-coding:utf-8-*-

  2. # script: CosineSimilarity.py

  3. __author__ = 'howie'

  4. import numpy as np

  5. from functools import reduce

  6. from math import sqrt



  7. class CosineSimilarity(object):

  8.    """

  9.    余弦相似性计算相似度

  10.    """


  11.    def __init__(self, initQuery, domainData):

  12.        self.title = initQuery

  13.        self.data = domainData


  14.    def create_vector(self):

  15.        """

  16.        创建单词向量

  17.        :return: wordVector = {} 目标标题以及各个网站标题对应的单词向量

  18.        """

  19.        wordVector = {}

  20.        for web, value in self.data.items():

  21.            wordVector[web] = []

  22.            titleVector, valueVector = [], []

  23.            allWord = set(self.title + value)

  24.            for eachWord in allWord:

  25.                titleNum = self.title.count(eachWord)

  26.                valueNum = value.count(eachWord)

  27.                titleVector.append(titleNum)

  28.                valueVector.append(valueNum)

  29.            wordVector[web].append(titleVector)

  30.            wordVector[web].append(valueVector)

  31.        return wordVector


  32.    def calculate(self, wordVector):

  33.        """

  34.        计算余弦相似度

  35.        :param wordVector: wordVector = {} 目标标题以及各个网站标题对应的单词向量

  36.        :return: 返回各个网站相似度值

  37.        """

  38.        resultDic = {}

  39.        for web, value in wordVector.items():

  40.            valueArr = np.array(value)

  41.            # 余弦相似性

  42.            squares = []

  43.            numerator = reduce(lambda x, y: x + y, valueArr[0] * valueArr[1])

  44.            square_title, square_data = 0.0, 0.0

  45.            for num in range(len(valueArr[0])):

  46.                square_title += pow(valueArr[0][num], 2)

  47.                square_data += pow(valueArr[1][num], 2)

  48.            squares.append(sqrt(square_title))

  49.            squares.append(sqrt(square_data))

  50.            mul_of_squares = reduce(lambda x, y: x * y, squares)

  51.            resultDic[web] = numerator / sum_of_squares

  52.        return resultDic


  53.    def get_website(self, resultDic):

  54.        """

  55.        获取最可能是官网的网站

  56.        :param resultDic: 各个网站相似度值

  57.        :return: 最可能的网站 也可能为空

  58.        """

  59.        website = ''

  60.        largest = max(zip(resultDic.values(), resultDic.keys()))

  61.        if largest[0]:

  62.            website = largest[1]

  63.        # 当相似度为0

  64.        else:

  65.            websites = [key for key, values in resultDic.items() if values == 0.0]

  66.            for eachWebsite in websites:

  67.                keyword = ','.join(self.data[eachWebsite]).lower()

  68.                if 'home' in keyword or "welcome" in keyword:

  69.                    website = eachWebsite

  70.        return website

3.总结

至此,若有更好的解决方案,欢迎赐教,谢谢。

往期推荐:

如何用PEP 8编写优雅的Python代码

Sanic中文教程合集:免费下载

如何用Python创建一个简单的神经网络

Python之装饰器


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存