使用request和re爬取豆瓣250排行榜信息
1.1 请求
请求,也就是由客户端发出,可以分成4部分:请求方法、请求的网址、请求头、请求体。
常见的请求方法有两种:GET和POST。
在浏览器里直接输入URL然后回车,这就发起了一个GET请求,请求的参数会直接包含到URL里。比如百度搜索时间,就是一个GET请求,对应链接:https://www.baidu.com/s?wd=时间 , 参数wd就是要搜索的关键字。POST请求多数用在表单的提交,也就是需要登录的时候,输入账号密码,点击“登录”,就会发起一个POST请求。
- GET和POST的区别:
- GET请求的参数会包含在URL里,数据可以在URL里看到,但是POST请求的URL不会包含数据,数据都是通过表单形式传输,会包含在请求体中。
- GET请求提交的数据最多1024字节,POST方式没有限制。
向百度发起get请求,并打印返回结果
import requests
r = requests.get('https://www.baidu.com')
print(type(r)
print(r.status_code)
print(r.text)
返回结果如下图
可以看到返回了状态码是200 以及网页的源码
断开网络再请求
ConnectionError: HTTPSConnectionPool(host='www.baidu.com', port=443): Max retries exceeded with url: /?name=germey&age=22 (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x00000286A1D56A90>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed',))
什么是请求头,如何添加请求头
请求头,用来说明服务器要使用的附加信息,比较重要的信息有Cookie、Referer、User-Agent等。
- Host:用于指定请求资源的主机IP和端口号
- Cookie:它的主要功能是维持当前访问会话。例如我们输入用户名和密码成功登陆某个网站,服务器会用会话保存登录状态信息,后面再刷新或请求站点内其他页面时,会发现都是登录状态,这就是Cookie的作用。
- Referer:用来识别这个请求是从哪里发来的,服务器可以拿到这个信息并做想要的处理,如做来源统计、防盗链处理等。
- User-Agent:简称UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。通常在写爬虫时要加上这个信息,伪装成浏览器,如果不加可能会被反爬。
所以,请求头是请求的重要组成部分,写爬虫的时候,多数情况下需要设置请求头。
如果要添加请求头信息,只需要传递一个dict就可以了。
>>> data = {
'name':'Ying'
}
r = requests.get('http://httpbin.org/get',parms=data)
print(r.text)
1.2 正则表达式
正则表达式是一个处理字符串非常强大的一个工具,它有自己的语法结构,学好这玩楞,实现字符串的检索、替换、匹配验证那就是分分钟的事情了。
常用的匹配规则
\w | 匹配字母、数字及下划线 |
---|---|
\W | 匹配不是字母、数字及下划线 |
\s | 匹配任意空白字符串,等价于[\t\n\r\f] |
\d | 匹配任意数字,等价于[0-9] |
· | 匹配任意字符,除了换位符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 |
* | 匹配0个或多个表达式 |
? | 匹配0个或1个前面的正则表达式 |
[^…] | 匹配不在[ ]中的字符,比如[^abc]匹配除了a、b、c之外的字符 |
{n} | 精确匹配n个前面的表达式 |
{n,m} | 匹配n到m次由前面正则表达式定义的片段,贪婪方式 |
需要注意的两个模式:
贪婪模式:(.)
非贪婪模式: ( .?)
1.3 爬取豆瓣电影250排行榜信息
使用requests和re爬取https://movie.douban.com/top250里的内容,要求抓取名次、影片名称、年份、导演等字段。
1.3.1 抓取分析
先瞧一瞧目标站点,可以看到一页是给出25个电影信息一共10页。还有就是到第二页是时候网址里的start=25,第三页就是start=50,一共10页,每一页的URL规律也就是:https://movie.douban.com/top250?start=?&filter=
?处是数字,从0开始依次增加25,所以获取排行榜信息,只需要分开访问10次,10次的start参数依次设置成0、25、50…225就可以了,在获取不同页面之后,再用正则表达式提取相关信息,就可以得到需要的信息了。
1.3.2 抓取首页
接下来进行代码实现。第一步,先抓取第一页的内容。emmm 定义一个get_one_page()方法,给它传入url参数。然后返回页面结果。
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
response = requests.get(url,headers=headers)
return response.text
def main():
url = 'https://movie.douban.com/top250?start=0'
html = get_one_page(url)
print(html)
main()
运行之后,就可以成功的获取第一页的源代码了。获取源代码之后开始解析页面,提取目标信息。
1.3.3 正则提取
开始解析网页提取信息
import re
def parse_one_page(html):
pattern = re.compile('<li>.*?<em class="">(\d+)</em>.*?info">.*?title">(.*?)</span>.*?</div>', re.S)
items = re.findall(pattern, html)
for item in items:
print(item)
我们要写匹配的正则表达式,()内的内容是我们要获取的,然后使用findall方法获取所有匹配的字符串。这里获取的是电影排名和名称。
完整的正则表达式为
<li>.*?<em class="">(\d+)</em>.*?info">.*?title">(.*?)</span>.*?<p class="">.*?导演:(.*?) (.*?)<br>.*?(\d+) / (.*?) / (.*?)\n.*?</p>.*?<span class="rating_num" property="v:average">(.*?)</span>.*?content="10.0"></span>.*?<span>(\d+)人评价</span>.*?</div>
爬取结果
emm顺便给把这些信息保存到本地
with open('result.txt', 'a', encoding='utf-8') as f:
f.write(json.dumps(content, ensure_ascii=False)+'\n')
f.close()
最后整合一下代码
import json
import requests
import re
import time
#for i in range(10):
#start = i*25
#url = 'https://movie.douban.com/top250?start='+str(start)
#print(url)
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
response = requests.get(url,headers=headers)
if response.status_code == 200:
return response.text
def parse_one_page(html):
pattern = re.compile(
'<li>.*?<em class="">(\d+)</em>.*?info">.*?title">(.*?)</span>.*?<p class="">.*?导演:(.*?) (.*?)<br>.*?(\d+) / (.*?) / (.*?)\n.*?</p>.*?<span class="rating_num" property="v:average">(.*?)</span>.*?content="10.0"></span>.*?<span>(\d+)人评价</span>.*?</div>',re.S
)
items = re.findall(pattern,html)
for item in items:
yield {
'index': item[0],
'title': item[1],
'director': item[2],
'actor': item[3],
'release_time': item[4],
'location': item[5],
'label': item[6],
'score': item[7],
'users': item[8]
}
#print(items)
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f:
f.write(json.dumps(content, ensure_ascii=False)+'\n')
f.close()
def main(start):
url = 'https://movie.douban.com/top250?start='+str(start)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)
#print(html)
if __name__ == '__main__':
for i in range(10):
main(i * 25)
总结
emmm似乎也没什么好总结的,正则表达式还是很好用的。