前言
本文主要以代码形式讲解爬虫,代码中有注释可助理解,代码都是可以运行的,或许有些网站变化,导致无法访问或者属性元素找不到,要想运行的话,自个在网站里找元素位置并在代码中更改。
代码都是在PyCharm编译下写的,读者也可以下个PyCharm,还是很好用的。
顺便说几个快捷键,都是对于选中的语句:
Tab #换行
Shift+Tab #取消换行
Ctrl+?键 #多行注释(取消注释)
爬虫分类
还是先说下分类吧(教科书式,)
• 通用爬虫:通用爬虫是搜索引擎(Baidu、Google、Yahoo等)“抓取系统”的重要组成部分。
主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。
简单来讲就是尽可能的;把互联网上的所有的网页下载下来,放到本地服务器里形成备分,在对这些网页做相关处理(提取关键字、去掉广告),最后提供一个用户检索接口。
• 聚焦爬虫:聚焦爬虫是根据指定的需求抓取网络上指定的数据。
例如:获取豆瓣上电影的名称和影评,而不是获取整张页面中所有的数据值。
• 增量式爬虫:增量式是用来检测网站数据更新的情况,且可以将网站更新的数据进行爬取
• 深层网络爬虫:大部分不能通过静态的URL获取,隐藏在搜索表单之后,只有用户提交一些关键词之后才能获得的网络页面。
requests爬取
爬取步骤:
1、指定url
2、发送请求
3、获取响应
4、数据解析
5、永久化存储
下面算是最基本的爬虫了,其中没有数据解析,看下爬取过程就行了
# -*- coding: utf-8 -*-
#加上面这个是防止中文乱码
import requests
#爬取河南理工大学主页
# 1 指定URL
get_url = "http://www.hpu.edu.cn/www/index.html"
#UA伪装
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
}
# 2 发送请求
response = requests.get(url = get_url, headers = header)
# 3 获取响应数据
response.encoding = 'utf-8'
page_text = response.text
# 4 永久化存储
fp = open("hpu.html",'w',encoding = 'utf-8')
fp.write(page_text)
fp.close()
get参数
下面代码是get请求时带有params参数,算是获取动态数据,获取到的是json类型的响应数据
# -*- coding: utf-8 -*-
import requests
import json
#豆瓣电影
url="https://movie.douban.com/j/chart/top_list?"
headers ={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"}
start = input("请输入起始位置:")
limit = input("请输入电影数量:")
params = {
'type': '24',
'interval_id': '100:90',
'action':'' ,
'start': start,
'limit': limit
}
response = requests.get(url=url,params=params,headers=headers)
movie = response.json()
fp = open("douban.json",'w',encoding='utf-8')
json.dump(movie,fp=fp,ensure_ascii=False)
fp.close()
response.close()
post请求参数
# -*- coding: utf-8 -*-
import requests
import json
#百度翻译
url="https://fanyi.baidu.com/sug"
headers ={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"}
word = input("请输入想要查询的单词:")
data = {
"kw": word
}
response = requests.post(url=url,data=data,headers=headers)
dic = response.json()
print(dic["data"][0]['k'])
print(dic["data"][0]['v'])
filename = word+".json"
fp = open(filename,'w',encoding='utf-8')
json.dump(dic,fp=fp,ensure_ascii=False)
fp.close()
运行结果:
生成的json文件,类型是字典:
数据解析
re用到的语句
import re
re.compile(pattern, flags=0) #将一个正则表达式模式编译为一个正则表达式对象
re.findall(pattern, string, flags=0) #匹配到字符串中所有符合条件的元素
re.finditer(pattern, string ,flags=0)
.groups() #匹配对象函数来获取匹配表达式。
.sprit() #去掉字符串里空格和换行
bs4语句
from bs4 import BeautifulSoup #调用库
page=BeautifulSoup(string,'lxml') #先将字符串转换为bs4识别的类型
table = page.find("table",class_="hq_table") #class属性:class_
table = page.find("table",attrs={"class":"hq_table"}) # #指定属性
trs = table.find_all("tr") #找到所有tr标签
#获取文本
.string
.text
.get_text()
.get('title') #用.get(‘属性值’)的方法获取标签里面的属性值
xpath语句
from lxml import etree
tree = etree.HTML(html) #解析
/div[@class='coll'] #指定属性
/text() #获取文本
/@href #获取属性
/
三个特点:
re正则表达式:具有灵活、逻辑性和功能性非常强的特点,能迅速地通过表达式从字符串中找到所需信息的优点,但对刚接触的人,比较晦涩难懂。
bs4:提供了一些简单的函数用来处理导航、搜索、修改分析树等功能,可为用户提供需要抓取的数据,非常简便,仅需少量的代码就可以写出一个完整的应用程序,不仅支持python标准库中的HTML解析器,还支持一些第三方的解析器。
xpath:选择功能十分强大,提供了非常简洁明了的路径选择表达式,提供了超过100个内建函数,用于字符串、数值、时间的匹配,以及节点、序列的处理等等,几乎所有定位的节点都可以用Xpath来选择。
re正则化
正则的语法:使用元字符进行排列组合用来匹配字符串
安装re库
.* 贪婪匹配,匹配所有符合的
.*? 惰性匹配 ,只匹配第一次成功
正则化匹配
# -*- coding: utf-8 -*-
import re
content = """
<div class='焦作'><span id='1001'>河南理工</span></div>
<div class='郑州'><span id='2001'>郑州大学</span></div>
<div class='开封'><span id='3001'>河南大学</span></div>
<div class='洛阳'><span id='4001'>河南科大</span></div>
"""
obj = re.compile(r"<div class='(.*?)><span id='.*?"
r"'>(?P<name>.*?)</span></div>",re.S) #re.S必须要带,不然.*不匹配
result = re.findall(obj,content)
print(result)
运行结果:
爬取豆瓣Top250前十页
# -*- coding: utf-8 -*-
import requests
import re
url = 'https://movie.douban.com/top250'
hd = {
'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Mobile Safari/537.36'
}
f = open("movie.txt",'w',encoding='utf-8')
for i in range(10):
n = i*25
params = {
'start': n,
'filter': ''
}
#url = url + '?start={}&filter='.format(n)
response = requests.get(url, headers=hd, params=params)
response.encoding = 'utf-8'
page_text = response.text
#print(page_text)
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">'
r'(?P<name>.*?)</span>.*?<p class="">.*?<br>(?P<year>.*?)'
r' .*?<div class="star">.*?property="v:average">(?P<score>.*?)</span>'
r'.*?<span>(?P<num>.*?)人评价</span>',re.S)
result = re.finditer(obj,page_text)
for res in result:
f.write(res.group("name"))
f.write('\t')
f.write(res.group("year").strip())
f.write('\t')
f.write(res.group("score"))
f.write('\t')
f.write(res.group('num'))
f.write('\n')
print('第{}页完成'.format(i+1))
f.close()
运行结果:
生成movie.txt文件:
bs4
bs4:通过标签的特征定位到想要获取的内容
安装BeautifulSoup库
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
(3)获取其标签内的内容
- soup.a.string
- soup.a.text
- soup.a.get_text()
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx") #属性值查找
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
#获取文本
.string
.text
.get_text()
.string可以返回当前节点中的内容,但是当前节点包含子节点时,.string不知道要获取哪一个节点中的内容,故返回空
.text(或.get_text())可以返回当前节点所包含的所有文本内容,包括当前节点的子孙节点
string与text:
bs4新发地代码实例
# -*- coding: utf-8 -*-
import requests
import csv
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
}
url="http://www.xinfadi.com.cn/marketanalysis/0/list/1.shtml"
resp = requests.get(url=url,headers=headers)
#print(type(resp.text))
page=BeautifulSoup(resp.text,'lxml')
#print(type(page))
# table = page.find("table",class_="hq_table") #class属性:class_
table = page.find("table",attrs={"class":"hq_table"}) #指定属性
#print(type(table),table)
trs = table.find_all("tr") #匹配所有tr标签
vegs = trs[1:]
#print(vegs[0].string)
fp = open("price.csv","w",newline='',encoding="utf-8") #有中文,utf-8编码格式,newline是防止多出现一行
csvwriter = csv.writer(fp)
for tr in vegs:
tds = tr.find_all("td")
#print(tds)
name = tds[0].string #菜名
lowest = tds[1].string #最低价
aver = tds[2].string #平均价
highest = tds[3].string #最高价
scale = tds[4].string #规格
unit = tds[5].string #单位
date = tds[6].string #发布日期
#print(name+","+lowest+','+aver)
csvwriter.writerow([name,lowest,aver,highest,scale,unit,date]) #csv保存方式
fp.close()
print('ok')
#爬取十页
# for i in range(10):
# url = f'http://www.xinfadi.com.cn/marketanalysis/0/list/{i + 1}.shtml'
# r = requests.get(url,headers=hd).content.decode('utf-8')
# page = BeautifulSoup(r,'lxml')
# tbody = page.find('div',class_='hangqing').find('table',class_='hq_table').findAll('tr')[1:]
# for tr in tbody:
# print(tr.text)
运行结果:
生成price.csv文件:
xpath
xpath用路径找到数据
安装lxml库
代码实例
# -*- coding: utf-8 -*-
from lxml import etree
html = """
<html lang="en">
<head>
<meta charset="UTF-8" />
<title> 河南理工大学 </title>
</head>
<body>
<div>
<p>计算机学院</p>
</div>
<div class = "dept">
<p id="bigdata">大数据</p>
<p>物联网</p>
<a href="http://www.hpu.edu.cn/" title = "计算机",target = "_self">
/<span>this is a span</span>
计算机学院成立于1999年</a>
<img src = "dfiles/11066/bwcx/dfiles/5455/images/top.png" alt="" />
</div>
<div class = "coll">
<ul>
<li><a href="http://www.sohu.com/" title = "souhu",target = "_self">搜狐</a></li>
<li><a href="http://www.zzu.edu.cn/" title = "zhengda",target = "_self">郑大</a></li>
<li><a href="http://www.hu.edu.cn/" class = "heda",target = "_self">河大</a></li>
<li><a href="http://www.baidu.edu.cn/" title = "baidu",target = "_self">百度</a></li>
<li><b>数据科学与大数据技术</b></li>
<li><i>计算机科学与技术</i></li>
</ul>
</div>
</body>
</html>
"""
tree = etree.HTML(html)
# print(tree)
# result1 = tree.xpath("/html")
# print(result1)
# result2 = tree.xpath("/html/head/title/text()")
# print(result2)
#[' 河南理工大学 ']
# result3 = tree.xpath("/html//li[1]/a/text()")
# print(result3)
#['搜狐']
result4 = tree.xpath("/html/body/div[@class='coll']/ul") #指定属性
# print(result4)
#[<Element ul at 0x2982ee2ff00>]
result5 = result4[0].xpath("./li") #./当前目录
# print(result5)
for i in result5:
# print(i)
# print(i.xpath("./*/text()"))
print(i.xpath("./a/@href")) #获取href属性
运行结果:
selenium
selenium是一个用电脑模拟人操作浏览器网页,可以实现自动化,测试等!还有就是只要是肉眼能在网页看到的selenium都能爬取,对动态数据爬取很是方便。
准备工作:
-
安装seleniumm
pip install selenium -
下载浏览器驱动
Firefox浏览器驱动:geckodriver
Chrome浏览器驱动:chromedriver
Edge浏览器驱动:MicrosoftWebDriver
打开本地浏览器,查看浏览器版本,然后下载对应的驱动器版本
下载后,(解压),把驱动器放到与python.exe同一目录下。用PyCharm的运行一个代码后有显示python.exe路径。
selenium模拟浏览器爬取拉勾网职位信息
# -*- coding: utf-8 -*-
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from lxml import etree
import time
#隐藏浏览器,就浏览器不弹出来
# opt = Options()
# opt.add_argument('--headless')
# opt.add_argument('--disable-gpu')
# web = Chrome(options=opt)
web = Chrome()
url = 'https://www.lagou.com/'
web.get(url)
web.find_element_by_xpath('//*[@id="changeCityBox"]/ul/li[1]/a').click() #选择城市
time.sleep(1)
web.find_element_by_xpath('//*[@id="search_input"]').send_keys('python',Keys.ENTER) #搜索JAVA,回车
for i in range(3):
#爬取3页
li_list = web.find_elements_by_xpath('//*[@id="s_position_list"]/ul/li') #每页所有职业,列表
for li in li_list:
time.sleep(1)
#//点进职业详细信息(就点击),这种方法以防页面有滚动,无法点击现象。
element = li.find_element_by_xpath('./div[1]/div[1]/div[1]/a/h3')
web.execute_script("arguments[0].click();", element) #这种方法防止找不到元素
#li.find_element_by_xpath('./div[1]/div[1]/div[1]/a/h3').click()
#//切换新打开的窗口,即职业详细信息窗口
handlers = web.window_handles
web.switch_to.window(handlers[-1])
time.sleep(1) #停一秒是防止页面没有加载出来,元素找不到而出错
job = web.find_element_by_class_name('position-head-wrap-position-name').text #职业名
company = web.find_element_by_class_name('company').text #公司名
salary = web.find_element_by_class_name('salary').text #薪水
adress = web.find_element_by_class_name('publish_time').find_element_by_class_name('company').text #公司地址
#下面try是找岗位职责,因为有些页面信息是折叠的,所以先判断下是否有可点击折叠元素
try:
web.find_element_by_xpath('//*[@id="container"]/div[1]/div[1]/div[1]/span').click()
duty = web.find_element_by_xpath('//*[@id="job_detail"]/dd[2]/div').text
except:
duty = web.find_element_by_xpath('//*[@id="job_detail"]/dd[2]/div').text
print(job,company,salary,adress[:-4],duty,'\n')
web.close() #关闭当前页面
time.sleep(1)
web.switch_to.window(handlers[0]) #转到最初那个窗口
web.find_element_by_class_name('pager_next ').click() #点击下一页
#web.close() #关闭浏览器
#web.quit() #关闭浏览器所有打开的窗口
运行结果:
若是出错,下面出错信息,不是代码问题,因为你他这个网址会记录你访问次数啥的,很烦人,体验下这个过程就行了。
验证码
有些登录操作需要验证码,本节讲selenium获取验证码过程
首先你需要个识别验证码的第三方工具,这里推荐个超级鹰,关注公众能白嫖使用许多次,够支持你学会了。
selenium验证码12306登录操作代码
# -*- coding: utf-8 -*-
import requests
from selenium.webdriver import Chrome
import time
from PIL import Image
from selenium.webdriver.common.action_chains import ActionChains
from hashlib import md5
url = 'https://kyfw.12306.cn/otn/resources/login.html'
web = Chrome()
web.get(url)
web.maximize_window() #窗口最大化
# web.refresh() #刷新
web.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a').click()
# time.sleep(1)
web.find_element_by_xpath('//*[@id="J-userName"]').send_keys('帐号') #这里需要输入你12306的帐号
web.find_element_by_xpath('//*[@id="J-password"]').send_keys('密码') #12306密码
web.save_screenshot('page_.png') #截全屏
img = web.find_element_by_xpath('//*[@id="J-loginImg"]') #找到验证码位置(通过属性定位)
loc = img.location
size = img.size #验证码大小
rect = (loc['x'],loc['y'],loc['x']+size['width'],loc['y']+size['height']) #验证码左上角和右下角坐标
i = Image.open('./page_.png')
code = './code_.png'
frame = i.crop(rect) #吧把验证码部分截下来
frame.save(code)
#下面类代码是超级鹰验证码识别框架,不用管
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
chaojiying = Chaojiying_Client('用户帐号', '密码', '918789') #帐号,密码用自己的 #用户中心>>软件ID 生成一个替换 96001
im = open('code_.png', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
clicks = chaojiying.PostPic(im, 9005)['pic_str'] #pic_str是字典里的一个键,值是验证码(点击坐标)
print(clicks)
loc_all = clicks.split('|')
#模拟图片验证码点击操作
for xy in loc_all:
x,y = xy.split(',')
ActionChains(web).move_to_element_with_offset(img,int(x),int(y)).click().perform()
web.find_element_by_xpath('//*[@id="J-login"]').click() #点击登录
#拖动滑块
time.sleep(1)
#web.switch_to.alert.accept() #切换到弹出框
huakuai = web.find_element_by_xpath('//*[@id="nc_1_n1z"]')
#time.sleep(1)
# move_to_gap(huakuai,get_track(300))
# 防止12306禁止selenium
#使用selenium滑动会被12306检测到,需要伪装一下
script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'
web.execute_script(script)
#滑块移动
span = web.find_element_by_xpath('//*[@id="nc_1_n1z"]')
action = ActionChains(web)
action.click_and_hold(span)
action.move_by_offset(350, 0).perform()
action.release()
print('登陆了成功!')
代码运行说明:
1、下面这个调一下,在设置里面。不调的话验证码截剪的不对
2、代码里需要填写两处帐号、密码,一个是12306、另一个是超级鹰的
3、出错可能验证码没有成功,这个代码只会测试一次,没成功就重新运行代码。
运行:
截的网页:
截剪的验证码:
输出:
好了,到此本片文章结束,希望你能有所收获,捏!