2. 最简爬虫
这里我们以优秀的python第三方库requests为例,该库是“唯一一个非转基因的Python HTTP库,人类可以安全享用”。
其中HTTP(HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,超文本文件传输时,必须遵守这个协议。
非转基因是作者幽默的说法,表明该库是原汁原味符合python设计理念,易用易于理解。
输入以下代码,并观察执行结果。
In [1]:
import requests
from bs4 import BeautifulSoup
import re
导入必要的模块
In [2]:
r = requests.get('https://github.com/liupengyuan/python_tutorial/blob/master/chapter1/0.md')
print(r.text[:1000])
3. 静态定向爬虫基础
本小节中的爬虫是开始就指定了要抓取网页的地址(定向),要抓取的内容直接存在在网页源代码中(静态),因此称为静态定向爬虫。
由于需要用到Chrome浏览器中的检查功能,因此需要读者下载安装Chrome浏览器。
3.1 抓取网易首页(www.163.com)上的全部新闻
In [3]:
r = requests.get('http://www.163.com/')
print(r.text[:1000])
检查
,可在Elements标签页面找到该新闻标题以及对应的链接在html代码中的位置。<li class="cm_fb">
<a href="http://news.163.com/xxxxxx.html">yyyyyyyyy</a>
::after
</li>
<li>
及</li>
之间,且没有其间没有换行符。
In [4]:
p = r'<li>(.+)?</li>'
contents = re.findall(p, r.text)
print('\n'.join(contents[:5]))
p = r'<li>(.+)?</li>'
构造了正则表达式,可提取<li>...</li>
之间的内容(不包括之间的换行符)re.findall(p, r.text)
将r.text中所有的符合上正则的提取出来放入列表最终打印前50行,可以发现后面有一些并非新闻标题与对应链接,并非所有在<li>...</li>
之间的内容都是目标内容
有关正则表达式,可参考教程:https://github.com/liupengyuan/python_tutorial/blob/master/chapter3 中的正则表达式快速教程。可以通过构造一些正则表达式来完成网页解析将目标内容提取。
from bs4 import BeautifulSoup
在开始导入
In [5]:
soup = BeautifulSoup(r.text, 'html.parser')
contents = soup.find_all('ul', attrs='cm_ul_round')
print('type is:', type(contents))
contents[:1]
Out[5]:
<ul class="cm_ul_round">
及</ul>
之间,且其他非新闻标题的内容,均不在这种规定了class的<ul>
标签之间
In [6]:
for line in contents:
url_titles = line.find_all('a')
for url_title in url_titles:
url = url_title.get('href')
title = url_title.string
print(type(url_title), url, title)
if input()=='b':
break
line.find_all('a')
取得其中在<a>
及</a>
标签对(该标签对表示超链接)中间的内容,url_titles的内容仍然是一个ResultSet对象,含有所有的url及titleTag
对象,利用Tag
对象的get('href')
方法,该方法可以取得标签的属性值,本例中参数为属性href
,其值为超链接的URLstring
属性,取得该超链接的文本
In [7]:
url_title_dict = {}
for line in contents:
url_titles = line.find_all('a')
for url_title in url_titles:
url = url_title.get('href')
title = url_title.string
if url and url.endswith(r'.html'):
try:
url_title_dict[url] = title
except KeyError:
continue
.html
的条目我们还要获取各链接下的新闻文本
In [ ]:
title_text_dict = {}
for url, title in url_title_dict.items():
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
html = soup.find('div', attrs = 'post_text')
if html:
passages = html.find_all('p')
text = ''
for passage in passages:
if passage.string:
text += passage.string
title_text_dict[url] = title, text
<div class = 'post_text>...</div>
中<p>...</p>
标签对之间(p即passage),因此需要利用find_all()方法提取之中所有的段落,放入passages中至此,形成了一个词典,key为新闻的url,value为对应的标题与正文,可将上述程序整合,并将抓取内容保存到文件中
In [1]:
import requests
from bs4 import BeautifulSoup
import re
def get_163_home_news(filename):
try:
r = requests.get(r'http://www.163.com')
except:
print('Can not get the page.\n')
return False
soup = BeautifulSoup(r.text, 'html.parser')
contents = soup.find_all('ul', attrs='cm_ul_round')
error_url = []
f_err = open('error_urls', 'w', encoding = 'utf-8')
fh = open(filename, 'w', encoding = 'utf-8')
for line in contents:
url_titles = line.find_all('a')
for url_title in url_titles:
url = url_title.get('href')
title = url_title.string
if url and url.endswith(r'.html'):
try:
r = requests.get(url)
except:
print('Error in getting:', url)
error_url.append(url)
continue
soup = BeautifulSoup(r.text, 'html.parser')
html = soup.find('div', attrs = 'post_text')
if html:
passages = html.find_all('p')
text = ''
for passage in passages:
if passage.string:
text += passage.string
fh.write('{}\n{}\n{}\n'.format(url, title, text))
f_err.write('\n'.join(error_url))
fh.close()
f_err.close()
return True
In [2]:
filename = r'news_163_home.txt'
get_163_home_news(filename)
Out[2]:
3.2 抓取有道词典查询词的双语例句
分析:
通过在有道词典(dict.youdao.com)进行单词查询,发现对任意单词xxxxx,给出该单词翻译信息的页面URL为:http://dict.youdao.com/w/xxxxx
由于我要抓取词xxxxx中查询结果的所有双语例句,则需要在页面下部的显示最后一个例句后,点击更多双语例句,这会更新当前页面的URL为:http://dict.youdao.com/example/blng/eng/xxxxx/#keyfrom=dict.main.moreblng 。所有双语例句均在其中,因此,我们只对这个URL进行爬取分析即可
<ul class='ol'>...</ul>
标签对之间。<p>...</p>
标签对之间
In [ ]:
import requests
from bs4 import BeautifulSoup
def get_word_sents(word):
url = r'http://dict.youdao.com/example/blng/eng/{}/#keyfrom=dict.main.moreblng'.format(word)
try:
r = requests.get(url)
except:
print('Can not get the page.\n')
return False
word_sents = []
soup = BeautifulSoup(r.text, 'html.parser')
contents = soup.find('ul', attrs = 'ol')
sents = contents.find_all('p')
for sent in sents:
word_sents.append(sent.text.replace('\n','')+'\n')
return word_sents
word_sents.append(sent.text.replace('\n','')+'\n')
,是为了写入文件时候较为整齐
In [ ]:
import os
test_word = '爬虫'
sents = get_word_sents(test_word)
with open(test_word+'.txt', 'w', encoding = 'utf-8') as f:
f.writelines(sents)
4. 动态页面的抓取(以后)
5. Scrapy爬虫框架(以后)