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

1.問題

對展會數據分類后,我的新任務是如何通過公司名、公司地址、國家等海關數據推斷出該公司的官網網站(若官網不存在則不考慮)
以下數據僅供參考:

公司名 國家 地址
JPW INDUSTRIES INC 427 NEW SANFORD RD LAVERGNE TN 37086 US
Fujian Xishi Co., Ltd CN, CHINA
BusinessPartner Co.,ltd
BENKAI Co.,Ltd
GOLD INC 18245 E 40TH AVE AURORA CO 80011 US

需要得到結果:

公司名 官方網站
JPW INDUSTRIES INC http://http://www.jpwindustries.com/
Fujian Xishi Co., Ltd http://www.xishigroup.com/
BusinessPartner Co.,ltd http://www.traderthailand.com/
BENKAI Co.,Ltd http://www.benkaico.com
GOLD INC https://goldbuginc.com/

2.解決

由數據可看出,公司名是絕對存在的,故解決思路是從公司名出發,而不怎么全面的國家以及地址信息則用來提高準確度。
大體思路是這樣的,若公司官網存在,那么通過搜索引擎定會被檢索到,搜索引擎自然首選google,所以可以先通過獲取谷歌搜索的結果,然后分析獲取的結果,從而得出最可能是該公司網站的url。
初步搜索一下,看看各種情況:
第一種情況,檢索即可很直觀地得出結果

JPW INDUSTRIES INC

第二種情況,檢索不能直觀地得出結果,但官網確實存在(第二檢索個結果
Fujian Xishi Co., Ltd

第三種情況,輸入公司名+公司地址和只輸入公司名得出的結果不一樣

GOLD INC

對于第三種情況,可以看出輸入公司名+公司地址得出的結果是絕對正確的。
觀察第三種情況,當輸入公司名+公司地址時,返回結果的右側會出現公司的詳細信息,經過驗證,若出現這種情況,則其website對應的url絕對正確。
故代碼的第一步驟可以首先檢索公司名+公司地址,觀察website元素是否存在,若存在,返回公司官網,否則,對公司名進行檢索。
代碼:

def searchWeb(query, tld='com', lang='en', tbs='0', num=10, safe='off', tpe='', user_agent=None):
    query = quote_plus(query)
    get_page(url_home % vars())
    url = url_search_num % vars()
    # Request the Google Search results page.
    html = get_page(url)
    try:
        href = re.findall(r'Directions</a><a\s*class="fl"\s*href="(.*?)".*?>Website', str(html))[0]
        link = filter_result(href)
        return link
    except:
        pass
    return None
直接返回公司官網

能直接獲取公司官網畢竟是少數,大多數據還是要通過一步步計算得出,主要經過以下步驟:
獲取搜索引擎檢索結果提取url->初步排除某些url->余弦相似度計算最可能的結果

2.1.獲取搜索引擎檢索結果提取url

對于谷歌搜索,我使用了MarioVilas的項目google,畢竟在國內,為了以防萬一我也寫了yahoo搜索,代碼如下:

#!/usr/bin/env
# -*-coding:utf-8-*-
# script: yahooSearch.py
__author__ = 'howie'

import sys
import time

if sys.version_info[0] > 2:
    from urllib.request import Request, urlopen
    from urllib.parse import quote_plus, urlparse
else:
    from urllib import quote_plus
    from urllib2 import Request, urlopen
    from urlparse import urlparse, parse_qs

try:
    from bs4 import BeautifulSoup

    is_bs4 = True
except ImportError:
    from BeautifulSoup import BeautifulSoup

    is_bs4 = False

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

headers = {
    'accept-encoding': 'gzip, deflate, sdch, br',
    'accept-language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4',
    'upgrade-insecure-requests': '1',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'cache-control': 'max-age=0',
    'authority': ' search.yahoo.com'
}
# 默認user_agent
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'


def filter_link(link):
    try:
        linkData = urlparse(link)
        if linkData.netloc and "yahoo.com" not in linkData.netloc and "/search/srpcache" not in linkData.path:
            return link
    except Exception:
        pass
    return None


def yahooSearch(query, start=0, num=10, page=1, pause=1.0):
    """
    獲取雅虎搜索url
    :param query: 搜索關鍵詞
    :param start: 開始條目,最好為0
    :param num: 搜索條目 建議10的倍數
    :param page: 頁數
    :param pause: 停頓時間
    :return: 返回url
    """
    query = quote_plus(query)
    while page > 0:
        url = url_search % vars()
        time.sleep(pause)
        request = Request(url)
        request.add_header('User-Agent', user_agent)
        response = urlopen(request)
        html = response.read()
        if is_bs4:
            soup = BeautifulSoup(html, 'html.parser')
        else:
            soup = BeautifulSoup(html)
        anchors = soup.find(id="web").find_all('a')
        for a in anchors:
            try:
                link = filter_link(a["href"])
                if link:
                    yield link
            except KeyError:
                continue
        start += num + 1
        page -= 1


if __name__ == '__main__':
    # GOLD INC 18245 E 40TH AVE AURORA CO 80011 US
    for url in yahooSearch("GOLD INC 18245 E 40TH AVE AURORA CO 80011 US"):
        print(url)

2.2.初步排除某些url

這個可根據個人需求來配置,可添加webConfig.py腳本,排除某些url:

# -*-coding:utf-8-*-
__author__ = 'howie'

config = dict(
    # www開頭或分割后數組大于二的網站
    forbid_www=["www.linkedin.com", "www.alibaba.com"],
    # 非www開頭的網站
    forbid=["imexbb.com", "made-in-china.com"]
)

以公司名BENKAI Co.,Ltd為例,初步獲取url:
['http://www.benkaico.com', 'http://hisupplier.com', 'http://www.hktdc.com/en']

2.3.余弦相似度計算最可能的結果

對于公司BENKAI Co.,Ltd,我們獲得了三個結果,現在又該如何從該列表中取得最可能的結果呢。
這里可以采用余弦相似度,具體公式可google,稍稍解釋下:
對于這三個網站:

['http://www.benkaico.com', 'http://hisupplier.com', 'http://www.hktdc.com/en']```
可以通過計算各個網站的`title`和`BENKAI Co.,Ltd`的相似程度來取得最可能的結果。
#####2.3.1:對各網站title進行分詞
```python
{'http://www.benkaico.com': ['benkai', 'co.', ',', 'ltd', '.'], 
'http://www.hktdc.com/en': ['hktdc.com', 'a\x80\x93', 'page', 'not', 'found'], 
'http://hisupplier.com': ['china', 'suppliers', ',', 'suppliers', 'directory', ',', 'china', 'manufacturers', 'directory', '-', 'hisupplier.com']}
2.3.2:構建單詞向量
{'http://www.benkaico.com': [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1]], 
'http://www.hktdc.com/en': [[1, 1, 0, 0, 1, 0, 0, 0, 1], [0, 0, 1, 1, 0, 1, 1, 1, 0]],
 '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:計算余弦相似度
{'http://www.benkaico.com': 0.94427190999915878, 
'http://www.hktdc.com/en': 0.0, 
'http://hisupplier.com': 0.31451985913875646}

通過比較,可以看到http://www.benkaico.com相似度最高的結果,跟真實結果一樣。

http://www.benkaico.com

全部步驟代碼如下:

# -*-coding:utf-8-*-
__author__ = 'howie'
from urllib import parse
from bs4 import BeautifulSoup
from collections import defaultdict
import requests
import re
import nltk
import time
from config.webConfig import config
from CosineSimilarity import CosineSimilarity
from search.yahooSearch import yahooSearch
from search.gooSearch import search, searchWeb


class Website(object):
    """
    通過公司名等信息獲取網站官網
    """

    def __init__(self, engine='google'):
        self.forbid_www = config["forbid_www"]
        self.forbid = config["forbid"]
        self.engine = engine

    def get_web(self, query, address=""):
        """
        獲取域名
        :param query: 搜索詞
        :param address: 在此加上地址時,query最好是公司名
        :return: 返回最可能是官網的網站
        """
        if self.engine == 'google' and address:
            allQuery = query + " " + address
            result = searchWeb(query=allQuery, num=5)
            if result:
                return result
        allDomain = self.get_domain(query)
        if len(allDomain) == "1":
            website = allDomain[0]
        else:
            # 初步判斷網站域名
            counts = self.get_counts(allDomain)
            largest = max(zip(counts.values(), counts.keys()))
            if largest[0] > len(allDomain) / 2:
                website = largest[1]
            else:
                # 獲取對應域名標題
                domainData = self.get_title(set(allDomain))
                # 計算相似度
                initQuery = nltk.word_tokenize(query.lower(), language='english')
                # 余弦相似性計算相似度
                cos = CosineSimilarity(initQuery, domainData)
                wordVector = cos.create_vector()
                resultDic = cos.calculate(wordVector)
                website = cos.get_website(resultDic)
        return website

    def get_domain(self, query):
        """
         獲取谷歌搜索后的域名
        :param query:搜索條件
        :return:域名列表
        """
        allDomain = []
        if self.engine == "google":
            for url in search(query, num=5, stop=1):
                allDomain += self.parse_url(url)
        elif self.engine == "yahoo":
            for url in yahooSearch(query):
                allDomain += self.parse_url(url)
        if not allDomain:
            allDomain.append('')
        return allDomain

    def parse_url(self, url):
        allDomain = []
        domainParse = parse.urlparse(url)
        # 英文網站獲取
        if "en" in domainParse[2].lower().split('/'):
            domain = domainParse[1] + "/en"
        else:
            domain = domainParse[1]
        domainList = domain.split('.')
        # 排除干擾網站
        if len(domainList) >= 3 and domainList[0] != "www":
            isUrl = ".".join(domain.split('.')[-2:])
            if isUrl not in self.forbid:
                allDomain.append(domainParse[0] + "://" + isUrl)
        elif domain not in self.forbid_www:
            allDomain.append(domainParse[0] + "://" + domain)
        return allDomain

    def get_title(self, setDomain):
        """
        獲取對應網站title,并進行分詞
        :param allDomain: 網站集合
        :return: 網站:title分詞結果
        """
        domainData = {}
        for domain in setDomain:
            headers = {
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
                "Accept-Encoding": "gzip, deflate, sdch",
                "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4",
                "Cache-Control": "max-age=0",
                "Proxy-Connection": "keep-alive",
                "Upgrade-Insecure-Requests": "1",
                "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",
            }
            try:
                data = requests.get(domain, headers=headers).text
                soup = BeautifulSoup(data, 'html.parser')
                title = soup.title.get_text()
                title = re.sub(r'\r\n', '', title.strip())
                titleToken = nltk.word_tokenize(title.lower(), language='english')
                domainData[domain] = titleToken
            except:
                pass
        return domainData

    def get_counts(self, allDomain):
        """
        返回網站列表各個域名數量
        :param allDomain: 網站列表
        :return: 網站:數量
        """
        counts = defaultdict(int)
        for eachDomain in allDomain:
            counts[eachDomain] += 1
        return counts


if __name__ == '__main__':
    # allQuery = ["National Sales Company, Inc.", "Decor Music Inc.","Fujian Xishi Co., Ltd","Kiho USA Inc.","BusinessPartner Co.,ltd","BENKAI Co.,Ltd"]
    # GOLD INC 18245 E 40TH AVE AURORA CO 80011 US
    allQuery = ["ALZARKI INTERNATIONAL"]
    website = Website(engine='google')
    for query in allQuery:
        time.sleep(2)
        website = website.get_web(query=query)
        print(website)

計算余弦相似度代碼

# -*-coding:utf-8-*-
# script: CosineSimilarity.py
__author__ = 'howie'
import numpy as np
from functools import reduce
from math import sqrt


class CosineSimilarity(object):
    """
    余弦相似性計算相似度
    """

    def __init__(self, initQuery, domainData):
        self.title = initQuery
        self.data = domainData

    def create_vector(self):
        """
        創建單詞向量
        :return: wordVector = {} 目標標題以及各個網站標題對應的單詞向量
        """
        wordVector = {}
        for web, value in self.data.items():
            wordVector[web] = []
            titleVector, valueVector = [], []
            allWord = set(self.title + value)
            for eachWord in allWord:
                titleNum = self.title.count(eachWord)
                valueNum = value.count(eachWord)
                titleVector.append(titleNum)
                valueVector.append(valueNum)
            wordVector[web].append(titleVector)
            wordVector[web].append(valueVector)
        return wordVector

    def calculate(self, wordVector):
        """
        計算余弦相似度
        :param wordVector: wordVector = {} 目標標題以及各個網站標題對應的單詞向量
        :return: 返回各個網站相似度值
        """
        resultDic = {}
        for web, value in wordVector.items():
            valueArr = np.array(value)
            # 余弦相似性
            squares = []
            numerator = reduce(lambda x, y: x + y, valueArr[0] * valueArr[1])
            square_title, square_data = 0.0, 0.0
            for num in range(len(valueArr[0])):
                square_title += pow(valueArr[0][num], 2)
                square_data += pow(valueArr[1][num], 2)
            squares.append(sqrt(square_title))
            squares.append(sqrt(square_data))
            sum_of_squares = reduce(lambda x, y: x * y, squares)
            resultDic[web] = numerator / sum_of_squares
        return resultDic

    def get_website(self, resultDic):
        """
        獲取最可能是官網的網站
        :param resultDic: 各個網站相似度值
        :return: 最可能的網站 也可能為空
        """
        website = ''
        largest = max(zip(resultDic.values(), resultDic.keys()))
        if largest[0]:
            website = largest[1]
        # 當相似度為0
        else:
            websites = [key for key, values in resultDic.items() if values == 0.0]
            for eachWebsite in websites:
                keyword = ','.join(self.data[eachWebsite]).lower()
                if 'home' in keyword or "welcome" in keyword:
                    website = eachWebsite
        return website

3.總結

至此,若有更好的解決方案,歡迎賜教,謝謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容