三个流程
- 获取网页
- 解析网页
- 存储数据
获取网页
获取网页的基础:requests、urllib、selenium
进阶:多线程、登录抓取、突破ip封禁、使用服务器抓取
解析网页
基础:re正则表达式、Beautiful、lxml
进阶:解决中文乱码
存储数据
基础:存入txt和csv文件
进阶:存入MySQL\MongoDB数据库
或者直接用Scrapy框架。
编写一个简单的爬虫
import requests
import time
from bs4 import BeautifulSoup
link="http://www.santostang.com//"
headers={'User-Agent':'Mozilla/5.0(Windows;U;Windows NT6.1;en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.7'}
r=requests.get(link,headers=headers)
print(r.text)
soup=BeautifulSoup(r.text,"html.parser")
time.sleep(10)
title=soup.find("h1",class_="post-title").a.text
print(title)
with open('title_test.txt',"a+") as f:
f.write(title)
学会用浏览器菜单栏中的检查菜单来查看浏览器的html语句
其中,requests.get说获取相应的相应内容。只使用它只能读取网页的一些设置值,如语言设置、响应状态值等等。
定制Requests
这一节主要是解决有些网页必须设置了部分参数之后才能进行获取,这部分的内容包括传递URL参数、定制请求头、发送POST请求、设置超时等等。
传递URL参数
在获取代码中修改成如下带有参数的请求
key_dict={'key1':'value1','key2':'value2'}
r=requests.get('http://httpbin.org/get',params=key_dict)
就形成了我们需要的URL格式
http://httpbin.org/get?key1=value1&key2=value2
定制请求头
这部分的内容需要使用浏览器的检查-Network 可以看到请求头的具体信息,将请求头的内容给到requests.get获取函数里面的headers部分即可。
发送POST请求
与GET的不同在于,get请求的参数回跟在URL后面,在有重要密码等参数时就很不安全。
发送POST请求的规则如下:
key_dict={'key1':'value1','key2':'value2'}
r=requests.post('http://httpbin.org/get',data=key_dict)
超时
限制访问网页的时间。
只要在请求函数后面加上
timeout= 具体值
爬取豆瓣代码
import requests
import time
from bs4 import BeautifulSoup
def get_movies():
headers={'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0',
'Host':'movie.douban.com'}
movie_list=[]
for i in range(0,10):
link='http://movie.douban.com/top250?start='+str(i*25)
r=requests.get(link,headers=headers,timeout=10)
time.sleep(1)
#print(str(i+1),"页相应状态码",r.status_code)
soup=BeautifulSoup(r.text,"lxml")
div_list=soup.find_all('div',class_='hd')
for each in div_list:
movie=each.a.span.text.strip()
movie_list.append(movie)
return movie_list
movies=get_movies()
print(movies)
动态网页抓取
在学习之前需要了解异步更新技术——AJAX,它的价值在于通过少量的代码就可以使网页实现异步更新,即网页的部分更新(内容变化,但url没变)。这样网页的抓取就显得繁复,我们可以以爬取网页的评论来了解动态网页抓取的过程。(一般用JavaScript)
一般用
- 浏览器审查元素解析地址
- 通过Selenium模拟浏览器抓取
浏览器审查
首先给出完整代码
import json
import requests
link="https://api-zero.livere.com/v1/comments/list?callback=jQuery112405130785675025886_1620273279720&limit=10&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1620273279722"
header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'}
r=requests.get(link,headers=header)
json_string=r.text
json_string=json_string[json_string.find('{'):-2]
json_data=json.loads(json_string)
comment_list=json_data['results']['parents']
for eachone in comment_list:
message=eachone['content']
print(message)
1.虽然数据没有出现在网页源代码中,但还是可以在网页-检查中找到所想要的数据,需要找到真正的评论文件进行爬取。
2.获取到数据以后发现是乱码,所以还需要用json进行解析。
json_string[json_string.find(’{’):-2]
就是仅仅提取字符串中符合json的部分,接着用json.loads可以把字串格式的响应体数据转化成json数据。利用其结构我们可以提取评论的列表comment_list。
通过Selenium模拟浏览器进行抓取
主要解决了之前那种方法找不到真是源代码的情况。可以模拟浏览器加载页面,同时抓取数据,这样就把动态页面的获取变成了静态。
1.用对应驱动打开页面,代码如下
from selenium import webdriver
driver = webdriver.Firefox(executable_path=r'C:\Users\Gray\Desktop\geckodriver.exe')
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
这一部分在jupyter中运行时可能报错,因为现在版本的selenium没有了geckodriver,要下载后运行上面程序(路径地址一定要搞对),运行之后就可以代开对应的网页,实现模拟浏览器。
comment=driver.find_element_by_css_selector('div.reply-content')
content=comment.find_element_by_tag_name('p')
这一部分是通过css选择器取取class为reply-content,tag为p的文本。
但是运行之后这部分的内容还是不对,这是因为原来代码中的javascript的内容解析到了一个iframe框架当中,所以找不到div.reply-content。因此还需要对框架进行解析
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))
comment=driver.find_element_by_css_selector('div.reply-content')
content=comment.find_element_by_tag_name('p')
selenium还可以模拟浏览器进行下滑和点击,本篇文章仅仅用来入门。
高级操作(待补充)
- 控制CSS的加载
- 控制图片文件的显示
- 控制JavaScript的运行
解析网页
- 实用正则表达式
- BeautifulSoup
- lxml
正则表达式
关于正则表达式的内容在我的另一篇文章里面有写,这里我们主要写怎么用来解析网页。
re.match
import re
m=re.match(‘www’,‘www.santostang.com’,flags=0)
print(m.span)
print(m.start)
print(m.end)
也能将第一个参数换成正则表达式的形式。(匹配时一般会采用贪婪匹配的形式),匹配之后通过group来调用。group(0)就是原来的字符串,1就是左边,1就是右边,无就是都取。在正则匹配时一般字符串需要经过字串转义和正则转义。
re.search
re.match只能从字串的起始位置开始匹配,而re.search可以扫描整个字符串并返回第一个成功的匹配。具体参数设置方式与re.match一样。
re.findall
它的优势在于可以找到所有匹配的结果并以列表显示出来,而前面两个只能找出一个。
re.findall(’<><>(.*?)<>’,text),只提取括号里面的内容。(还需要后续学习html之后补充)
BeautifulSoup
from bs4 import BeautifulSoup
它是一个工具箱,提供了标准库中的HTML解析器html.parser、和一些第三方解析器,如"lmxl"
第三方解析器还有[“lmxl”,“xml”]、“html5lib”(容错性最好)等等
方法
import requests
from bs4 import BeautifulSoup
link="http://www.santostang.com//"
headers={'User-Agent':'Mozilla/5.0(Windows;U;Windows NT6.1;en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.7'}
r=requests.get(link,headers=headers)
soup=BeautifulSoup(r.text,"lxml")
first_title=soup.find("元素名",class="具体class").a.text.strip()
还可以
soup.find_all(“元素名”,class=“具体class”)
返回一个列表。
其他功能
美化:soup.prettify()
遍历文档数
soup.header.h1
soup.header.div.contents #把它的子节点用列表方式输出
soup.header.div.descendants #列出子子孙孙的节点
.parent #列出父节点
搜索文档树
就是前面用的find\find_all
CSS选择器
它既可以作为遍历树也可以作搜索。
soup.select(“header h1”)
也可以通过某个tag下的直接子标签遍历
soup.select(“div>a”) #即div下所有的a标签
当搜索用时
soup.select(‘a[href^=“http://www.santostang.com/”]’)#搜索以http://www.santostang.com/开始的
使用lxml解析网页
需要安装lxml库
import requests
from lxml import etree
link="http://www.santostang.com//"
headers={'User-Agent':'Mozilla/5.0(Windows;U;Windows NT6.1;en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.7'}
r=requests.get(link,headers=headers)
html=etree.HTML(r.text)
title_list=html.xpath('//h1[@class="post-title"]/a/text()')
print(title_list)
"//h1"代表选取所有
子元素,后面加上class选取class=post-title的元素,/a表示选取h1子元素的a元素,/text() 表示提取a元素中的所有文本。
XPATH的查找方法,用Chrome检查,在要提取的数据那右键复制xPath.
数据保存
- 存在文件当中,txt和csv
with open(r'<文件地址>',"a+") as f:
f.write(<保存的内容>)
f.close()
import csv
with open('test.csv','r',encoding='UTF-8') as csvfile:
csv_reader=csv.reader(csvfile)
import csv
output_list=["1","2"]
with open('test2.csv','a+',encoding="UTF-8",newline='') as csvfile:
w=csv.writer(csvfile)
w.writerow(output_list)
- 存在数据库当中,MySQL和MongoDB
数据库的指令可以参考我的另一篇文章。
这里只介绍与本节有关的内容:
直接给出代码:
import pymysql
#打开数据库连接
db=pymysql.connect("localhost","root","password","数据库名称")
cursor=db.cursor()
#sql插入
sql="""INSERT INT ..........
"""
try:
#sql语句执行
cursor.execute(sql)
#提交到数据库执行
db.commit()
#如果发生错误就回滚
except:
db.rollback()
db.close
关于MongoDB的内容以后需要使用再进行补充