Python3使用BeautifulSoup解析百度关键词搜索结果

因为想看一下自己网站的关键词在百度搜索结果中的排名,所以,使用Python3编写了一个脚本进行爬取分析。

代码非常简单,水平一般(娇羞中),分享出来和大家交流。

先来说说实现过程吧!

一、百度搜索关键词的URL规则

例如:搜索本站域名“opython.com”。

搜索之后,点开搜索结果的第2页,URL类似:

https://www.baidu.com/s?wd=opython.com&pn=10&oq=opython.com&tn=baiduhome_pg&ie=utf-8&rsv_idx=2&rsv_pq=a99fa2280001734f&rsv_t=2b57Fux0BL2%2BdMJ6OjiZ6HP%2BE3OuYBUcSTarswtNJIr9qgkep2qSgUAMF0GOxB59Ja1o

为什么是第2页呢?

因为第1页看不到分页的参数。

这个URL是很长的一段内容,但实际上有用的就2个。

大家能够看到“https://www.baidu.com/s?”后面都是参数,真正有用的是“wd”和“pn”,也就是关键词和页码。

也许大家不太理解,为什么第2页的URL中参数“pn”的值是“10”。

这是因为搜索结果每一页的记录条数是10,这个数字代表当前页的搜索记录“id”是从10之后开始。

当我们了解了上述内容之后,就能够非常清楚的知道爬取某一个关键词搜索结果每一页的URL如何生成了。

格式:https://www.baidu.com/s?wd=[关键词]&pn=[页码减1乘10]

二、搜索结果的关键元素

百度的搜索结果页源代码中包含10条搜索记录。

但是,大家查看的时候,不要在浏览器中点鼠标右键选择查看源代码的选项,那样什么都看不到。

需要使用谷歌浏览器(Chrome)的检查或者火狐浏览器(Firefox)的审查元素功能才可以。

每一条搜索结果记录的内容类似:

id="1"srcid="1599" tpl="se_com_default" data-click="{"rsv_bdr":"0","p5":1}">...省略部分代码...style="text-decoration:none;">opython.com/

...省略部分代码...百度快照

代码太长,省略了无用的内容。

在保留的关键内容中,大家注意红字部分。

每条搜索结果记录的“id”是页面中唯一的,我们可以通过“id”来检查每条搜索记录是否包含查询的域名(关键词)。

但是,“id”是搜索记录最外层“

”标签的属性,我们还需要找到内部包含真实URL的“”标签,获取它包含的字符串进行匹配。

这个包含真实URL的“”标签,有一个“class”属性“c-showurl”,通过这个属性我们就能够比较准确的找到这个标签,并获取它内部包含的字符串。

不过要注意的是,获取到的字符串中会包含“”标签,需要先清除掉。

因为有时被加粗的只是真实URL的一部分,导致我们不能获取到真实的地址。

三、模拟浏览器打开URL的行为

如果直接使用urllib库中request模块的urlopen()方法打开需要爬取的百度搜索页,是不能正常获取页面内容的。

得到的结果类似:

为了能够正常的获取搜索结果内容,我们需要模拟浏览器打开网页的行为。

这里使用request模块中的build_opener()方法,创建一个打开URL的工具对象,

示例代码:

opener = request.build_opener()

然后,为这个对象添加“header”信息。

示例代码:

opener.addheaders = [('User-agent', # 添加模拟浏览器访问的header信息 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11')]

“header”信息中的“User-Agent”是用于告诉远程服务器,客户端的软件环境。

最后,通过对象的open()方法打开URL获取页面内容。

示例代码:

四、使用BeautifulSoup解析HTML

获取到的HTML内容,我们需要从中取得想要的信息。

BeautifulSoup是一个非常好用的用于解析HTML和XML的第三方库。

安装命令:

pip install beautifulsoup4

注意:使用这个库是“from bs4 import XXX”。

使用方法:

1、创建格式化后的文档对象

示例代码:

soup = BeautifulSoup(html, 'lxml')

注意:第二个参数“’lxml’”表示解析的是HTML。

2、通过文档对象进行查询操作

示例代码:

soup.p # 获取文档对象中第1个

标签的内容soup.p.string # 获取文档中第1个

标签包含的字符串soup.find(id=1) # 获取文档对象中第1个“id”属性为“1”的标签soup.find('a', 'c-showurl') # 获取文档对象中“class”属性为“c-showurl”的标签

更多使用帮助,请查看官方中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

五、最终实现

有了以上内容的支撑,我们就可以动手编写全部代码了。

示例代码:

from urllib import requestfrom urllib import parsefrom bs4 import BeautifulSoup # 用于解析HTMLimport multiprocessing # 用于多进程class BaiduRanking: # 定义爬取排名的类 def __init__(self, words, page_number, domain): self.words = words # 查询的关键词列表 self.page_number = page_number # 查询的页面数量 self.domain = domain # 查询的域名 self.opener = request.build_opener() # 创建打开URL的工具对象 self.opener.addheaders = [('User-agent', # 添加模拟浏览器访问的header信息 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11')] self.position = [] # 域名在搜索引擎查询结果中出现的位置列表 def get_urls(self, word): # 定义每个关键词查询页面的URL列表生成器 urls = [] for pn in range(self.page_number): url = 'https://www.baidu.com/s?wd=%s&pn=%s' % (parse.quote_plus(word), pn * 10) # 注意关键词转换格式 urls.append(url) yield urls def get_result(self, url, position, current_page): # 定义获取排名结果的方法 print(url) try: html = self.opener.open(url).read().decode().replace('', '').replace('', '') # 获取页面内容并替换掉字体加粗标签 soup = BeautifulSoup(html, 'lxml') # 解析HTML for i in range(current_page * 10 + 1, current_page * 10 + 11): # 轮询查询结果的id try: if self.domain in soup.find(id=i).find('a', 'c-showurl').string: # 判断从查询结果中找到的网址字符串是否包含域名 position.append(i) # 添加查询结果id到位置列表 except: # 发生异常时继续查询 continue except: pass # 某页面发生异常时忽略 def run_task(self): # 定义运行爬取任务的方法 for word in self.words: # 遍历关键词列表 position = multiprocessing.Manager().list() # 创建多进程共享的列表变量 current_page = 0 # 当前查询的页面编号 print('正在查询关键词:%s' % word) for urls in self.get_urls(word): # 遍历当前关键词需要爬取的所有URL processes = multiprocessing.Pool(5) # 开启进程池 for url in urls: # 遍历当前关键词的所有URL processes.apply_async(self.get_result, args=(url, position, current_page)) # 为每个URL爬取任务创建进程 current_page += 1 # 页面编号自增 processes.close() # 关闭进程池 processes.join() # 等待进程结束 print('查询完毕。\n出现位置:%s' % sorted(position)) # 显示输出排序后的爬取结果if __name__ == '__main__': domain = 'opython.com' # 查询的域名 words = ['Django2教程', 'Python新手教程'] # 查询的关键词列表 page_number = 10 # 每个关键词爬取的页面数量 ranking = BaiduRanking(words=words, domain=domain, page_number=page_number) # 实例化爬取排名的对象 ranking.run_task() # 运行爬取任务

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180701A0LHNF00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券