目录
1.正则表达式
参考:http://www.runoob.com/regexp/regexp-tutorial.html
这里面很详细介绍了正则表达式,我这里就不再赘述。
2.Python正则表达式
参考:http://www.runoob.com/python/python-reg-expressions.html
Pyhon的re 模块使 Python 语言拥有全部的正则表达式功能。
3.xpath爬取豆瓣top250电影
任务说明:
结合requests、re两者的内容爬取豆瓣电影 Top 250里的内容 要求抓取名次、影片名称、国家、导演等字段。
3.1查看xpanth信息
(1)谷歌浏览器 右键关键字选择检查
复制xpath信息:
xpath信息如下:
//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
然后套路代码看看:
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()')
print(info)
结果:
我们再看看第二部电影的电影信息xpath:
//*[@id="content"]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[1]
跟第一部对比:
//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
我们发现il[]里面发现了变换,写个代码看看:
我们打印前10名的电影信息:
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
for i in range(20):
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i]
print(info)
部分截图如下:
3.2xpath获取电影其他信息
(1)同样的操作我们检查一样电影名,
xpath如下:
//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a/span[1]
代码:
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
print(title)
结果:
(2)评分
xpath:
//*[@id="content"]/div/div[1]/ol/li[15]/div/div[2]/div[2]/div/span[2]
代码:
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for movies in scores:
print(movies)
结果如下:
(3)名次
xpath:
//*[@id="content"]/div/div[1]/ol/li[14]/div/div[1]/em
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
for i in range(20):
rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')[i]
print(rank)
结果:
3.3 信息切片
我们在获取影片信息的时候是这样的:
我们想把导演的名字获取出来。
3.3.1 replace()
首先介绍一下python replace()的方法:
(1)replace()方法语法:
str.replace(old, new[, max])
(2)参数:
old -- 将被替换的子字符串。
new -- 新字符串,用于替换old子字符串。
max -- 可选字符串, 替换不超过 max 次
(3)返回:
返回字符串中的 old(旧字符串) 替换成 new(新字符串)后生成的新字符串,如果指定第三个参数max,则替换不超过 max 次。
(4)我们把空格去掉:
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "")
原结果:
去掉后:
然后我们把换行去掉:
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "").replace("\n", "")
结果:
3.3.2 split()
split()通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则仅分隔 num+1 个子字符串
(1)语法:
str.split(str="", num=string.count(str))
(2)参数:
str -- 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
num -- 分割次数。默认为 -1, 即分隔所有。
我这里用默认值分割:
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "").replace("\n", "").split()
结果:
这样我们就可以把导演和主演分别提取出来了。
同样的方法我把国家也导出来,代码如下:
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[1]
info= info .replace(" ", "").replace("\n", "").split('/')[1]
print(info)
结果:
3.4信息汇总
我们把这些信息一起输出,首先我们输出某一部电影的所有信息
from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
#影片名
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
#
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
#导演
info1= info[0] .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info[0] .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info[1].replace(" ", "").replace("\n", "").split('/')[1]
#评分
rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
#名次
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for i in range(25):
info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
#导演
info1= info_1 .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info_1 .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {} ".format(title[i],score[i],rank[i],info1,info2,info3))
这里我提一句,上面的info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')这种语句出来的Info会有两个值,第一个是导演等信息,第二个是国家等信息,所以我这段话的info分奇数和偶数处理。
结果如下:
3.5余下页数信息汇总
上面的我只把第一页的影片信息汇总了,接下来我们看看怎么换页。比较一下不同页的URL:
第一页:https://movie.douban.com/top250?start=0
第二页:https://movie.douban.com/top250?start=25
第三页:https://movie.douban.com/top250?start=50
第四页:https://movie.douban.com/top250?start=75
......
代码:
from lxml import etree
import requests
for page in range(10):
url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25)
data=requests.get(url).text
s=etree.HTML(data)
#影片名
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
#
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
#导演
info1= info[0] .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info[0] .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info[1].replace(" ", "").replace("\n", "").split('/')[1]
#评分
rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
#名次
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for i in range(25):
info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
#导演
info1= info_1 .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info_1 .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {} {} ".format(title[i],scores[i],rank[i],info1,info2,info3))
这样运行出现了BUG:
原因是第214部影片没有演员信息,所以list Out of range
为了解决这个问题我加了一个判定语句,全部代码如下:
from lxml import etree
import requests
for page in range(10):
url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25)
data=requests.get(url).text
s=etree.HTML(data)
#影片名
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
#
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
#导演
info1= info[0] .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info[0] .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info[1].replace(" ", "").replace("\n", "").split('/')[1]
#评分
rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
#名次
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for i in range(25):
info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
if(len(info_1 .replace(" ", "").replace("\n", "").split())==2):
#导演
info1= info_1 .replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info_1 .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {} {} ".format(title[i],scores[i],rank[i],info1,info2,info3))
else:
#导演
info1= info_1 .replace(" ", "").replace("\n", "").split()[0]
#国家
info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {}".format(title[i],scores[i],rank[i],info1,info3))
现在程序完美运行~部分结果截图如下:
看到排名我还有点疑惑,为什么排第215的分数有9.2,216的分数有8.8,我还以为是我这里出了问题,去网站看了一下也是这样,难道不是按照分数排的吗...
3.5改进
这里我们默认电影名字以及评分都是完整的、正确的信息,这种默认一般情况下是没问题的。但其实是有缺陷的。如果我们少爬了或者多爬了信息,就会发生匹配的错误,那么该怎么避免这种错误呢?
仔细思考下,发现我们若是以电影名字为单位,分别获取对应的信息,那么匹配肯定完全正确。 电影名字的标签肯定在这部电影的框架内,于是我们从电影名字的标签往上找,发现覆盖整部电影的标签,把xPath信息复制下来
//*[@id="content"]/div/div[1]/ol/li[1
全部代码如下:
from lxml import etree
import requests
for page in range(10):
url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25)
data=requests.get(url).text
s=etree.HTML(data)
file = s.xpath('//*[@id="content"]/div/div[1]/ol/li')
for div in file:
#影片名
title=div.xpath('./div/div[2]/div[1]/a/span[1]/text()')
#
info=div.xpath('./div/div[2]/div[2]/p[1]/text()')
#评分
rank=div.xpath('./div/div[1]/em/text()')
#名次
scores=div.xpath('./div/div[2]/div[2]/div/span[2]/text()')
if(info[0] .replace(" ", "").replace("\n", "").split()==2):
#导演
info1= info[0].replace(" ", "").replace("\n", "").split()[0]
#主演
info2= info[0] .replace(" ", "").replace("\n", "").split()[1]
#国家
info3= info[1].replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {} {} ".format(title[0],scores[0],rank[0],info1,info2,info3))
else:
#导演
info1= info_1 .replace(" ", "").replace("\n", "").split()[0]
#国家
info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
print("{} {} {} {} {}".format(title[i],scores[i],rank[i],info1,info3))
完美运行~
4.正则表达式爬取豆瓣top250
上面我阴差阳错学习了xpath的方法,实际上我本来打算用正则表达式完成以上任务,现在补起来。
4.1 获取网站响应内容
import re, requests
url = "https://movie.douban.com/top250?"
movie_NO = movie_name = movie_count = movie_director = [] # 初始化数据,保证名称循环都是空的
req = requests.get(url)
# print(req.content.decode("utf-8")) # 打印接口请求到的信息
html_reponse = req.content.decode("utf-8")
print(html_reponse)
部分截图如下:
4.2 从响应内容里正则化提取所需内容
(1) re.findall()
首先我们来看看这个函数的用法:
findall(pattern, string, flags=0)
返回string中所有与pattern相匹配的全部字串,返回形式为数组
(2)提取名次
我们看名次在这里:
所以本页用这个语句查询:
movie_NO = re.findall('<em class="">(.*?)</em>', html_reponse) # 名次
结果如下:
(3)影片名
如果我们这样写:
movie_title=re.findall('<span class="title">(.*?)</span>', html_reponse)
那结果如图:
原因是
每个影片有几个题目,但是我们只要第一个就行。
movie_title = re.findall(
'[a-zA-Z]+://[^\s]*/" class="">\n <span class="title">(.*?)</span>',
html_reponse) # 影片名称#
print(movie_title)
(3)导演
movie_director = re.findall('导演: (.*?)[ |<br>]', html_reponse)
(4)国家
movie_count = re.findall(' / (.*?) / ', html_reponse) # 国家
(5)全部代码:
import re, requests
for i in range(10): # 一共有10页数据
url = "https://movie.douban.com/top250?start=" + str(i * 25) + "&filter="
movie_NO = movie_name = movie_count = movie_director = [] # 初始化数据,保证名称循环都是空的
req = requests.get(url)
# print(req.content.decode("utf-8")) # 打印接口请求到的信息
html_reponse = req.content.decode("utf-8")
movie_NO = re.findall('<em class="">(.*?)</em>', html_reponse) # 名次
# print(movie_NO)
# print(len(movie_NO))
movie_name = re.findall(
'[a-zA-z]+://[^\s]*/" class="">\n <span class="title">(.*?)</span>',
html_reponse) # 影片名称
# print(movie_name)
# print(len(movie_name))
movie_count = re.findall(' / (.*?) / ', html_reponse) # 国家
# print(movie_count)
# print(len(movie_count))
movie_director = re.findall('导演: (.*?)[ |<br>]', html_reponse) # 导演 (这里用竖杆把两种可能的都匹配到了,在此坑躺了两个小时!!)
# print(movie_director)
# print(len(movie_director))
for j in range(25):
# 电影的信息拼接起来
movie_info = "排名:" + movie_NO[j] + " 电影名:" + movie_name[j] + " 国家:" + movie_count[j] + " 导演:" + \
movie_director[j]
print(movie_info)
结果: