相信大家都遇到一个问题——百度搜索的结果可能50%都是广告,今天我们尝试搭建一个个性化搜索引擎。
我们使用网络爬虫来解决该问题,从自动搜索、抓取网页,提取网页内容、按照个性化匹配内容,输出保存,模拟搜索引擎的工作工程。
Python在网络爬虫方面也有很多成熟的库,也有很好的框架可以提供。
Python网络爬虫
Python网络爬虫,通过Python自动获取url的网页html内容,然后用正则表达式分析html,得到你想要的内容,包括url、文字、图片等。
如果必要,就可以对网页内url进行分析,进一步爬取,直到获得自己想要的内容。
当然通过文本语义分析,判断该网页是否是自己想要的内容也很重要。
Python爬取百度首页
今天我们用python3自带的urllib获取url内容。urllib是一组处理URLs的包,其中request模块可以打开和读取url链接。
我们看一下最简单的百度首页爬取示例。
from urllib import request
response = request.urlopen("http://www.baidu.com/") #打开百度首页
html = response.read() #读取响应值
html = html.decode('utf-8') #需要解码decode后,才能正常显示。编码方式可以通过网页查询
我们通过可以查看html的详细内容,如
In[23]: html[:20]
Out[23]: '<!DOCTYPE html>\n<!--'
也可以保存为本地html文件,离线查看。
Python处理html
Chorme浏览器开发者模式
Python可以用正则表达式提取想要的内容,在这之前建议通过chorme分析下待爬取的网页。
假如我们想提取百度首页右上角常用导航名称及url。通过开发者模式右上角箭头点击网页感兴趣内容,可以快速定位到源代码部分。
我们看一下该部分html源码:
<div id="u1">
<a href="http://news.baidu.com" name="tj_trnews" class="mnav" saprocessedanchor="true">新闻</a>
<a href="http://www.hao123.com" name="tj_trhao123" class="mnav" saprocessedanchor="true">hao123</a>
<a href="http://map.baidu.com" name="tj_trmap" class="mnav" saprocessedanchor="true">地图</a>
......
</div>
正则表达式处理HTML
然后我们就可以很容易写出提取标题的url和标题的正则表达式:
#没有办法一次性提取,先提问导航栏的全部内容
patten_u1 = re.compile('<div id="u1">.*?</div>',re.S)
item = patten_u1.findall(html)
#然和提取每一个导航链接
patten = re.compile(r'<a href="(?P<url>.*?)".*?class="mnav".*?>(?P<title>.*?)</a>',re.S)
items = patten.findall(item[0])
我可以看一下输出结果,非常完美。
In[50]:items
Out[50]:
[('http://news.baidu.com', '新闻'),
('http://www.hao123.com', 'hao123'),
('http://map.baidu.com', '地图'),
('http://v.baidu.com', '视频'),
('http://tieba.baidu.com', '贴吧'),
('http://xueshu.baidu.com', '学术')]
BeautifulSoup快速处理HTML
前面我们说过,Python的强大之处就是有很多“轮子”,可以直接拿来使用。正则表达式处理网页还是非常复杂的,不同网页必须重新写,而已经有人造好BeautifulSoup轮子,可以快速处理HTML,转换为Python对象,直接处理。
BeautifulSoup的使用有很多教程,需要大家自己学习,我也刚刚入门。
上面的功能可以快速实现:
from bs4 import BeautifulSoup #导入库
soup = BeautifulSoup(html, 'lxml') #转换html文件为BS对象
#安装逻辑选择,首先过滤 mnav,然和遍历,分别获取url和名称
items =[(i.attrs['href'],i.text) for i in soup.select('.mnav')]
结果和之间一样。大家可以尝试一下soup.select('.mnav')、soup.select('.mnav')[0].attrs的内容,理解BeautifulSoup的使用技巧。
Python实现个性化搜索引擎
上面已经基本了解Python爬虫的基本操作和所需知识,下面我尝试搭建个性化搜索引擎爬虫的基本框架。
经过初步分析,应该有以下几个功能点:
- 接受若干关键字,以空格区分,用百度搜索网页
- 分析网页内容,提取网页标题、url、简介,快速过滤广告、和关键字无关url
- 输出目标url、标题和简介供用户参考。
最终代码如下:
from urllib import request
from urllib import parse
from bs4 import BeautifulSoup
import pandas as pd
import re
import sys
#给定url,获取对应html,已经解码后
def get_html(url):
head = {}
head['User-Agent'] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36"
#模拟浏览器,可以简单避免反爬虫
req = request.Request(url, headers=head)
response = request.urlopen(req)
html = response.read()
#获取网页编码方式
charset = response.info().get_param('charset')
html = html.decode(charset)
return html
#从百度搜索页面结果,获取下一页url地址
def get_next_url(html):
soup = BeautifulSoup(html, 'lxml')
n = soup.select('.n')
if len(n) ==1:
url = n[0].attrs['href']
elif len(2) ==2:
url = n[1].attrs['href']
return "https://www.baidu.com"+url
#用百度网页搜索关键字,返回前nums页的html
def get_baidu_keywb(wb,nums):
url = "https://www.baidu.com/s?wd={}".format(parse.quote(wb))
htmls = []
html = get_html(url)
htmls.append(html)
#读取下一页,直到指定页数
i = 1
while(i<nums):
url_next = get_next_url(html)
html = get_html(url_next)
htmls.append(html)
i = i+1
return htmls
#分析处理htmls,得到一个表格,列名:标题、简介、详细url
def anay_htmls(htmls):
rows = []
for html in htmls:
soup = BeautifulSoup(html, 'lxml')
for i in soup.select('.result'):
patten = re.compile(r'{"title":"(.*?)","url":"(.*?)"}',re.S)
if 'c-abstract' in str(i):
data_tools = patten.findall(i.select('.c-tools')[0].attrs['data-tools'])[0]
title = data_tools[0]
detail_url = data_tools[1]
text = i.select('.c-abstract')[0].text
row = [title,text,detail_url]
rows.append(row)
data = pd.DataFrame(rows,columns=['title','text','detail_url'])
return data
#按照关键字过滤网页,按照某种优先级排序,这里算法可以很智能,需要搭建完善
#可以增加更多更智能的处理逻辑
def filter_urls(data,wb):
key_wb = wb.split(" ")
#百度关键字搜索不一定对,如搜广东移动,可能出现移动,进一步强匹配
isbaoliu = []
for i in data.index:
flag = True #是否保留标志
title = data.loc[i]['title']
text = data.loc[i]['text']
#多个关键字同时出现在标题或简介中
for key in key_wb:
#只要一个关键字不在 title和text中,置否
if (key not in title) & (key not in text):
flag =False
break
isbaoliu.append(flag)
data_out = data[isbaoliu]
return data_out
if __name__ =='__main__':
if len(sys.argv) > 2:
nums = int(sys.argv[1])
wb = ' '.join(sys.argv[2:])
htmls = get_baidu_keywb(wb,nums) #用百度查询网页
data = anay_htmls(htmls) #分析网页
data_out = filter_urls(data,wb) #过滤网页
#输出过滤后结果,仅作测试,真实代码可以不要这句
print("搜索过滤后结果{}条,比百度搜索少{}%。".format(len(data_out),'%0.2f' % (100*(len(data)-len(data_out))/float(len(data)))))
filename = "查询结果_"+wb.replace(' ','_') +".csv"
data_out.to_csv(filename,encoding = 'utf-8-sig',index = False)
else:
print("请输入查询页数及关键字,以空格区分关键字,比如\npython crawler_demo.py 2 广东移动 IPTV")
然后可以在命令行执行我们的个性化搜索引擎。
上面广告已经自动过滤调,同时减少68%的无效信息,个性化搜索引擎可以更适合自己,毕竟自己写的哈。
当然这个程序还有很多完善的地方,比喻语义分析相关,有一个分词的库——jieba分词,我同样没有时间去学。欢迎大家补充。
Python有个强大的爬虫框架Scrapy。非常强大,我也没时间去学。这些都期待你们的分享。
下一期,我们聊聊用Python进行网站搭建——简明Python开发教程(6):用Django搭建网站(https://www.jianshu.com/p/f5eb5a6864ed)。