Python 爬虫案例
Posted wespten
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 爬虫案例相关的知识,希望对你有一定的参考价值。
一、用cookie池模拟登录
在网络请求交互中,为了维持用户的登录状态,引入了cookie的概念。当用户第一次登录某个网站时,网站服务器会返回维持登录状态需要用到的信息,这些信息就称为cookie。浏览器会将cookie信息保存在本地计算机中,再次对同一网站发起请求时就会携带上cookie信息,服务器从中可以分析判断出用户的登录状态。
服务器中的资源有些不需要登录就能获取,有些则需要登录才能获取,如果在爬虫程序中携带正确的cookie信息,就可以爬取那些需要登录才能获取的数据了。
1、用浏览器获取cookie信息
代码文件:用浏览器获取cookie信息.py
第一次登录一个网页后,浏览器会从响应头的set-cookie字段中读取cookie值并保存起来。下次访问该网页时,浏览器就会携带cookie值发起请求,服务器从cookie值中得到用户登录信息,就会直接返回用户登录之后的页面。下面以人人网为例讲解如何获取cookie值。
在谷歌浏览器中打开人人网(http://www.renren.com/),输入账号和密码,登录成功后通过开发者工具对数据进行抓包,即在开发者工具的“Network”选项卡下刷新当前页面后选中第一个数据包,在“Headers”选项卡下的“Request Headers”中查看Cookie字段,该字段的值就是发起请求时携带的cookie值,如下图所示。
在爬虫程序中使用requests模块的get()函数发起请求时,携带cookie值的方式有两种,下面分别介绍。
第一种方式是在get()函数的参数headers中直接携带cookie值的字符串。
演示代码如下:
1 import requests
2 url = 'http://www.renren.com/974388972/newsfeed/photo'
3 cookie = 'anonymid=kbbumh2lxrmpf1; depovince=GW; _r01_=1; JSESSIONID=abcGWVhbBr19FhENjsNkx; ick_login=989861b2-b103-4dec-acae-a997d02bbaa6; taihe_bi_sdk_uid=f5185c56d440f6d9816cbd80ee1c01e3; taihe_bi_sdk_session=b57a76d842fee2d91ce5701999cb1dbe; XNESSESSIONID=abcoL9H_VgmAddSCksNkx; ick=c1409cb0-0813-44ec-8834-729af8f535cd; t=d06e12b9149594f6fba8cc62f9e8f3e32; societyguester=d06e12b9149594f6fba8cc62f9e8f3e32; id=974598592; xnsid=58ab78d8; WebOnLineNotice_974598592=1; jebecookies=3b9d3b87-1f8b-4867-a20b-b183cc3b36c4|||||; ver=7.0; loginfrom=null; wp_fold=0'
4 headers = 'Cookie': cookie, 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/73.0.3683.75 Safari/537.36' # 在headers中携带cookie值
5 response = requests.get(url=url, headers=headers).text # 获取页面源代码
6 print(response)
第二种方式是将获取到的cookie值的字符串处理成字典,作为get()函数的参数cookies携带上。
演示代码如下:
1 import requests
2 url = 'http://www.renren.com/974388972/newsfeed/photo'
3 cookie = 'anonymid=kbbumh2lxrmpf1; depovince=GW; _r01_=1; JSESSIONID=abcGWVhbBr19FhENjsNkx; ick_login=989861b2-b103-4dec-acae-a997d02bbaa6; taihe_bi_sdk_uid=f5185c56d440f6d9816cbd80ee1c01e3; taihe_bi_sdk_session=b57a76d842fee2d91ce5701999cb1dbe; XNESSESSIONID=abcoL9H_VgmAddSCksNkx; ick=c1409cb0-0813-44ec-8834-729af8f535cd; t=d06e12b9149594f6fba8cc62f9e8f3e32; societyguester=d06e12b9149594f6fba8cc62f9e8f3e32; id=974598592; xnsid=58ab78d8; WebOnLineNotice_974598592=1; jebecookies=3b9d3b87-1f8b-4867-a20b-b183cc3b36c4|||||; ver=7.0; loginfrom=null; wp_fold=0'
4 headers = 'Cookie': cookie, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36'
5 cookie_dict =
6 for i in cookie.split(';'): # 将cookie值的字符串格式化为字典
7 cookie_keys = i.split('=')[0]
8 cookie_value = i.split('=')[1]
9 cookie_dict[cookie_keys] = cookie_value
10 response = requests.get(url=url, headers=headers, cookies=cookie_dict).text # 将cookie字典作为参数cookies的值,获取页面源代码
11 print(response)
2、自动记录cookie信息
代码文件:自动记录cookie信息.py
上面介绍用浏览器获取cookie信息的方式需要我们自己在数据包中定位cookie信息并提取出来,相对比较烦琐。
这里介绍一种自动记录登录数据的方法:先使用requests模块中的Session对象对一个网站发起登录请求,该对象会记录此次登录用到的cookie信息;再次使用该对象对同一网站的其他页面发起请求时,就会直接使用记录下的cookie信息,不再需要额外携带cookie信息参数了。
演示代码如下:
1 import requests
2 s = requests.Session() # 实例化一个Session对象
3 headers = 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36' # 验证浏览器身份的信息
4 data = 'email': '15729560000', 'password': '123456' # 用于登录网站的账号/密码信息
5 response=s.post(url='http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2020451418538', data=data, headers=headers) # 使用Session对象携带前面设置好的浏览器身份信息和账号/密码信息,对人人网中需要登录的一个页面发起请求
6 print(response.cookies) # 查看cookie值
7 response1 = s.get(url='http://www.renren.com/974388972/profile?v=info_timeline', headers=headers).text # 再次使用Session对象对人人网中另一个需要登录的页面发起请求,Session对象会自动使用上次记录的cookie值,所以这次不需要携带账号/密码信息也能登录成功
8 print(response1) # 获取到需要登录后才能查看的页面的源代码
使用这种方式需要设置登录表单中账号和密码的字段名,也就是第4行代码中的'email'和'password'。使用谷歌浏览器的开发者工具可以提取登录表单中的字段名。
在谷歌浏览器中打开人人网首页,输入账号和密码,打开开发者工具,在“Network”选项卡下勾选“Preserve log”复选框,这样就能在登录跳转时保存登录表单的数据包,单击“登录”按钮,如下图所示。
登录成功后,在“Network”选项卡下的“Name”窗格中产生了很多新的数据包,其中就有登录表单的数据包,包名中一般有“login”字样,可在“Filter”筛选器中输入关键词“login”来筛选数据包。
筛选后可在“Name”窗格中看到含有“login”字样的数据包,单击这个包,在“Headers”选项卡下的“Form Data”中可以看到登录时携带的表单信息,一般只需要账号和密码的字段名(这里为“email”和“password”),在发起post请求时携带即可,如下图所示。
二、爬取当当网销售排行榜
代码文件:案例:爬取当当网的图书销售排行榜.py
图书销售排行榜对于出版社编辑制定选题开发方向、图书销售商制定进货计划具有很高的参考价值。实体书店的图书销售排行数据采集难度较大,而电子商务网站的图书销售排行数据则具有真实性高、更新及时、容易获取等优点,是一个相当好的数据来源。
步骤1:先确定需要爬取数据的网址。在谷歌浏览器中打开当当网,进入“图书畅销榜”,网址为http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-24hours-0-0-1-1。为了增强数据的时效性,选择查看近30日的排行榜数据,发现网址随之变为http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-1,该网址显示的是排行榜的第1页内容。
通过观察页面底部的翻页链接可以发现,排行榜共有25页,单击第2页的链接,可以发现网址的最后一个数字变为2,依此类推。根据这一规律,如果要爬取所有页码,只需使用for语句构造循环,按页码依次进行网址的拼接并发起请求即可。
演示代码如下:
1 import requests
2 import pandas as pd
3 from bs4 import BeautifulSoup
4 headers = 'User-Agent':' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' # 模拟浏览器的身份验证信息
5 for i in range(1, 26):
6 url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-i' # 循环生成每一页的网址
步骤2:确定数据是否是动态加载的。打开开发者工具,切换到“Network”选项卡,然后单击“All”按钮,按【F5】键刷新页面,在“Name”窗格中找到主页面的数据包并单击(通常为第一个数据包)。
在“Name”窗格的右侧切换至“Response”选项卡,并在网页源代码的任意处单击,按快捷键【Ctrl+F】调出局部搜索框,输入排行第一的图书名称,如“你当像鸟飞往你的山”,搜索后发现在“Response”选项卡下的网页源代码中有该关键词。这说明数据不是动态加载的,可以用响应对象的text属性获取网页源代码,再从源代码中提取数据。
在步骤1的for语句构造的循环内部继续添加如下代码:
1 response = requests.get(url=url, headers=headers, timeout=10) # 对25页的不同网址循环发起请求,设置超时等待为10秒
2 html_content = response.text # 获取网页源代码
3 soup = BeautifulSoup(html_content, 'lxml') # 将网页源代码实例化为BeautifulSoup对象
4 parse_html(soup) # 调用自定义函数提取BeautifulSoup对象中的数据,该函数的具体代码后面再来编写
5 print(f'第i页爬取完毕')
如果用局部搜索找不到关键词,则说明数据是动态加载的,需要携带动态参数发送请求,再使用json()函数提取数据。
步骤3:分析要提取的数据位于哪些标签中。利用开发者工具可看到要提取的排行榜数据位于class属性值为bang_list的<ul>标签下的多个<li>标签中,如下图所示。
每个<li>标签中存储着一本图书的详细数据,包括排名、书名、作者、出版时间、出版社、价格等。还需要继续利用开发者工具更细致地剖析源代码,定位这些数据所在的标签。具体方法和前面类似,这里就不展开讲解了,留给读者自己练习。
步骤4:编写提取数据的代码。先创建一个字典data_info,然后根据上一步的分析结果编写自定义函数parse_html(),从BeautifulSoup对象中提取数据并添加到字典中。
演示代码如下:
1 data_info = '图书排名': [], '图书名称': [], '图书作者': [], '图书出版时间': [], '图书出版社': [], '图书价格': [] # 新建一个空字典
2 def parse_html(soup): # 解析每一个BeautifulSoup对象
3 li_list = soup.select('.bang_list li') # 通过层级选择器定位class属性值为bang_list的标签下的所有<li>标签
4 for li in li_list: # 将从每一个<li>标签中解析到的数据添加到字典data_info相应键的对应列表中
5 data_info['图书排名'].append(li.select('.list_num ')[0].text.replace('.', ''))
6 data_info['图书名称'].append(li.select('.name a')[0].text)
7 data_info['图书作者'].append(li.select('.publisher_info ')[0].select('a')[0].text)
8 data_info['图书出版时间'].append(li.select('.publisher_info span')[0].text)
9 data_info['图书出版社'].append(li.select('.publisher_info ')[1].select('a')[0].text)
10 data_info['图书价格'].append(float(li.select('.price .price_n')[0].text.replace('¥', '')))
第7、8、9行代码使用层级选择器通过class属性值publisher_info进行标签定位,因为有两个<div>标签的class属性都是这个值,所以先做列表切片再使用select()函数进行定位,才能获得准确的数据。总体来说,如果多个标签的属性值一样,可以多次使用select()函数进行定位,这样获得的数据更准确。
第10行代码是为后面的数据清洗做准备,将“¥”替换为空值(即删除),再用float()函数将字符串转换为浮点型数字,以方便比较数据大小。
需要注意的是,在最终的代码文件中,定义parse_html()函数的代码需位于调用该函数的代码之前。步骤5:缺失值和重复值的处理。获取了每一页的数据并存储到字典data_info中后,还需要进行数据清洗。先用pandas模块将字典转换为DataFrame对象格式,再判断缺失值和重复值。
演示代码如下:
1 book_info = pd.DataFrame(data_info)
2 print(book_info.isnull()) # 缺失值判断
3 print(book_info.duplicated()) # 重复值判断
第3行代码中的duplicated()函数用于判断是否有重复行,如果有,则返回True。运行后,结果全为False,说明代码没有问题,获取到的数据也没有重复值和缺失值,接着进行异常值的处理。
步骤6:异常值的处理。假定本案例只研究价格在100元以下的图书,所以需要将价格高于100元的图书删除。
演示代码如下:
1 book_info['图书价格'][book_info['图书价格'] > 100] = None # 将大于100的图书价格替换为空值
2 book_info = book_info.dropna() # 删除有空值的一行数据
步骤7:保存爬取的数据。最后,将处理好的数据保存起来,这里存储为csv文件。
演示代码如下:
1 book_info.to_csv('当当网图书销售排行.csv', encoding='utf-8', index=False)
三、CSDN数据采集
通过python实现csdn页面的内容采集是相对来说比较容易的,因为csdn不需要登陆,不需要cookie,也不需要设置header。
本案例使用python实现csdn文章数据采集,获取我的博客下每篇文章的链接、标题、阅读数目。
需要安装BeautifulSoup包(点击下载)
python3.6下:
#coding:utf-8
#本实例用于获取指定用户csdn的文章名称、连接、阅读数目
import urllib
import re
from bs4 import BeautifulSoup
#csdn不需要登陆,也不需要cookie,也不需要设置header
print('=======================csdn数据挖掘==========================')
urlstr="https://blog.csdn.net/qq_35029061?viewmode=contents"
host = "https://blog.csdn.net/qq_35029061" #根目录
alllink=[urlstr] #所有需要遍历的网址
data=
def getdata(html,reg): #从字符串中安装正则表达式获取值
pattern = re.compile(reg)
items = re.findall(pattern, html)
for item in items:
urlpath = urllib.parse.urljoin(urlstr,item[0]) #将相对地址,转化为绝对地址
if not hasattr(object, urlpath):
data[urlpath] = item
print(urlpath,' ', end=' ') #python3中end表示结尾符,这里不换行
print(item[2], ' ', end=' ')
print(item[1])
#根据一个网址获取相关连接并添加到集合中
def getlink(url,html):
soup = BeautifulSoup(html,'html5lib') #使用html5lib解析,所以需要提前安装好html5lib包
for tag in soup.find_all('a'): #从文档中找到所有<a>标签的内容
link = tag.get('href')
newurl = urllib.parse.urljoin(url, link) #在指定网址中的连接的绝对连接
if host not in newurl: # 如果是站外连接,则放弃
continue
if newurl in alllink: #不添加已经存在的网址
continue
if not "https://blog.csdn.net/qq_35029061/article/list" in newurl: #自定义添加一些链接限制
continue
# if not "https://blog.csdn.net/qq_35029061/category_10969287" in newurl: #必须是python类目下的列表
# continue
# if not "https://blog.csdn.net/qq_35029061/category_11852875" in newurl: # 必须是K8s类目下的列表
# continue
alllink.append(newurl) #将地址添加到链接集合中
#根据一个网址,获取该网址中符合指定正则表达式的内容
def craw(url):
try:
request = urllib.request.Request(url) #创建一个请求
response = urllib.request.urlopen(request) #获取响应
html = str(response.read(),'utf-8') #读取返回html源码,,python2里面读取的是字节数组
# reg = r'"link_title"><a href="(.*?)">\\r\\n(.*?)\\n.*?</a>' #只匹配文章地址和名称
reg = r'"link_title"><a href="(.*?)">\\r\\n (.*?) \\r\\n.*?</a>[\\s\\S]*?阅读</a>\\((.*?)\\)</span>' # 匹配地址、名称、阅读数目
getdata(html,reg)
getlink(url,html)
except urllib.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)
for url in alllink:
craw(url)
python2.7下:
#coding:utf-8
#本实例用于获取指定用户csdn的文章名称、连接、阅读数目
import urllib2
import re
from bs4 import BeautifulSoup
#csdn不需要登陆,也不需要cookie,也不需要设置header
print('=======================csdn数据挖掘==========================')
urlstr="http://blog.csdn.net/luanpeng825485697?viewmode=contents"
host = "http://blog.csdn.net/luanpeng825485697" #根目录
alllink=[urlstr] #所有需要遍历的网址
data=
def getdata(html,reg): #从字符串中安装正则表达式获取值
pattern = re.compile(reg)
items = re.findall(pattern, html)
for item in items:
urlpath = urllib2.urlparse.urljoin(urlstr,item[0]) #将相对地址,转化为绝对地址
if not hasattr(object, urlpath):
data[urlpath] = item
print urlpath,' ', #print最后有个逗号,表示输出不换行
print item[2], ' ',
print item[1]
#根据一个网址获取相关连接并添加到集合中
def getlink(url,html):
soup = BeautifulSoup(html,'html.parser') #使用html5lib解析,所以需要提前安装好html5lib包
for tag in soup.find_all('a'): #从文档中找到所有<a>标签的内容
link = tag.get('href')
newurl = urllib2.urlparse.urljoin(url, link) #在指定网址中的连接的绝对连接
if host not in newurl: # 如果是站外连接,则放弃
continue
if newurl in alllink: #不添加已经存在的网址
continue
if not "http://blog.csdn.net/luanpeng825485697/article/list" in newurl: #自定义添加一些链接限制
continue
alllink.append(newurl) #将地址添加到链接集合中
#根据一个网址,获取该网址中符合指定正则表达式的内容
def craw(url):
try:
request = urllib2.Request(url) #创建一个请求
response = urllib2.urlopen(request) #获取响应
html = response.read() #读取返回html源码
# reg = r'"link_title"><a href="(.*?)">\\r\\n(.*?)\\n.*?</a>' #只匹配文章地址和名称
reg = r'"link_title"><a href="(.*?)">\\r\\n (.*?) \\r\\n.*?</a>[\\s\\S]*?阅读</a>\\((.*?)\\)</span>' # 匹配地址、名称、阅读数目
getdata(html,reg)
getlink(url,html)
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason
for url in alllink:
craw(url)
四、糗事百科数据采集
通过python实现糗事百科页面的内容采集是相对来说比较容易的,因为糗事百科不需要登陆,不需要cookie,不过需要设置http的MIME头,模拟浏览器访问才能正常请求
本案例使用python实现糗事百科数据采集,获取糗事百科热门的文章内容和好评数量。
需要安装BeautifulSoup包(点击下载)
python2.7下:
#coding:utf-8
#本实例用于获取糗事百科热门的文章内容和好评数量。
import urllib2
import re
from bs4 import BeautifulSoup
#糗事百科需要设置MIME头才能正常请求,不需要登陆,也不需要cookie
print('=======================糗事百科数据挖掘==========================')
urlstr="https://www.qiushibaike.com/8hr/page/%d"
data=
def getdata(html): #从字符串中安装正则表达式获取值
soup = BeautifulSoup(html, 'html.parser');
alldiv = soup.find_all("div", class_="content") #内容的外部div
allnum = soup.find_all("span", class_="stats-vote") #点赞数量的外部span
for i in range(0,len(alldiv)):
print str(alldiv[i].find_all('span')[0]).replace('<span>','').replace('</span>','').replace('<br/>','\\r\\n').strip() #内容文字,使用string在文字里还有<br/>时,无法打印,使用text会省略调用<br/>
print allnum[i].find_all('i')[0].string #好评数量
#根据一个网址,获取该网址中符合指定正则表达式的内容
def craw(url):
try:
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = 'User-Agent' : user_agent #设置MIME头,糗事百科对这个进行了验证
request = urllib2.Request(url,headers = headers) #创建一个请求
response = urllib2.urlopen(request) #获取响应
html = response.read() #读取返回html源码
getdata(html)
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason
for i in range(1,14):
url = urlstr % i
print(url)
craw(url)
python3.6下:
#coding:utf-8
#本实例用于获取糗事百科热门的文章内容和好评数量。
import urllib
from bs4 import BeautifulSoup
#糗事百科需要设置MIME头才能正常请求,不需要登陆,也不需要cookie
print('=======================糗事百科数据挖掘==========================')
urlstr="https://www.qiushibaike.com/8hr/page/%d"
data=
def getdata(html): #从字符串中安装正则表达式获取值
soup = BeautifulSoup(html, 'html.parser');
alldiv = soup.find_all("div", class_="content") #内容的外部div
allnum = soup.find_all("span", class_="stats-vote") #点赞数量的外部span
for i in range(0,len(alldiv)):
print(str(alldiv[i].find_all('span')[0]).replace('<span>','').replace('</span>','').replace('<br/>','\\r\\n').strip()) #内容文字,使用string在文字里还有<br/>时,无法打印,使用text会省略调用<br/>
print(allnum[i].find_all('i')[0].string) #好评数量
#根据一个网址,获取该网址中符合指定正则表达式的内容
def craw(url):
try:
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = 'User-Agent' : user_agent #设置MIME头,糗事百科对这个进行了验证
request = urllib.request.Request(url,headers = headers) #创建一个请求
response = urllib.request.urlopen(request) #获取响应
html = response.read() #读取返回html源码
getdata(html)
except urllib.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)
for i in range(1,14):
url = urlstr % i
print(url)
craw(url)
五、百度贴吧数据采集
通过python实现百度贴吧页面的内容采集是相对来说比较容易的,因为百度贴吧不需要登陆,不需要cookie,不需要设置http的MIME头
本案例使用python实现百度贴吧数据采集,获取百度贴吧的文章内容,楼层
百度贴吧网址比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,这是一个关于NBA50大的盘点,分析一下这个地址。
http:// 代表资源传输使用http协议
tieba.baidu.com 是百度的二级域名,指向百度贴吧的服务器。
/p/3138733512 是服务器某个资源,即这个帖子的地址定位符
see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真
所以我们可以把URL分为两部分,一部分为基础部分,一部分为参数部分。
例如,上面的URL我们划分基础部分是 http://tieba.baidu.com/p/3138733512,参数部分是 ?see_lz=1&pn=1
爬虫过程比较简单,基本还是围绕:请求、正则解析、打印存储。
注意:python3.4以后中,将urllib2、urlparse、robotparser并入了urllib模块,并且修改了urllib模块,其中包含了5个子模块,每个子模块中的常用方法如下:
python3中的库 包含了子类(python2中)
urllib.error: ContentTooShortError;URLError;HTTPError
urllib.parse: urlparse;_splitparams;urlsplit;urlunparse;urlunsplit;urljoin;urldefrag;unquote_to_bytes;unquote;parse_qs;parse_qsl;unquote_plus;quote;quote_plus;quote_from_bytes;urlencode;to_bytes;unwrap;splittype;splithost;splituser;splitpasswd;splitport等;
urllib.request: urlopen; install_opener; urlretrieve; urlcleanup; request_host; build_opener; _parse_proxy; parse_keqv_list; parse_http_list; _safe_gethostbyname; ftperrors; noheaders; getproxies_environment; proxy_bypass_environment; _proxy_bypass_macosx_sysconf; Request
urllib.response: addbase; addclosehook; addinfo;addinfourl;
urllib.robotparser: RobotFileParser
python2.7下:
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
#处理页面标签类
class Tool:
#去除img标签,7位长空格
removeImg = re.compile('<img.*?>| 7|')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\\t
replaceTD= re.compile('<td>')
#把段落开头换为\\n加空两格
replacePara = re.compile('<p.*?>')
#将换行符或双换行符替换为\\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
def replace(self,x):
x = re.sub(self.removeImg,"",x)
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\\n",x)
x = re.sub(self.replaceTD,"\\t",x)
x = re.sub(self.replacePara,"\\n ",x)
x = re.sub(self.replaceBR,"\\n",x)
x = re.sub(self.removeExtraTag,"",x)
#strip()将前后多余内容删除
return x.strip()
#百度贴吧爬虫类
class BDTB:
#初始化,传入基地址,是否只看楼主的参数
def __init__(self,baseUrl,seeLZ,floorTag):
#base链接地址
self.baseURL = baseUrl
#是否只看楼主
self.seeLZ = '?see_lz='+str(seeLZ)
#HTML标签剔除工具类对象
self.tool = Tool()
#全局file变量,文件写入操作对象
self.file = None
#楼层标号,初始为1
self.floor = 1
#默认的标题,如果没有成功获取到标题的话则会用这个标题
self.defaultTitle = u"百度贴吧"
#是否写入楼分隔符的标记
self.floorTag = floorTag
#传入页码,获取该页帖子的代码
def getPage(self,pageNum):
try:
#构建URL
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
#返回UTF-8格式编码内容
return response.read().decode('utf-8')
#无法连接,报错
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"连接百度贴吧失败,错误原因",e.reason
return None
#获取帖子标题
def getTitle(self,page):
#得到标题的正则表达式
pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
result = re.search(pattern,page)
if result:
#如果存在,则返回标题
return result.group(1).strip()
else:
return None
#获取帖子一共有多少页
def getPageNum(self,page):
#获取帖子页数的正则表达式
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None
#获取每一层楼的内容,传入页面内容
def getContent(self,page):
#匹配所有楼层的内容
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
contents = []
for item in items:
#将文本进行去除标签处理,同时在前后加入换行符
content = "\\n"+self.tool.replace(item)+"\\n"
contents.append(content.encode('utf-8'))
return contents
def setFileTitle(self,title):
#如果标题不是为None,即成功获取到标题
if title is not None:
self.file = open(title + ".txt","w+")
else:
self.file = open(self.defaultTitle + ".txt","w+")
def writeData(self,contents):
#向文件写入每一楼的信息
for item in contents:
if self.floorTag == '1':
#楼之间的分隔符
floorLine = "\\n" + str(self.floor) + u"-----------------------------------------------------------------------------------------\\n"
self.file.write(floorLine)
self.file.write(item)
self.floor += 1
print(item)
def start(self):
indexPage = self.getPage(1)
pageNum = self.getPageNum(indexPage)
title = self.getTitle(indexPage)
self.setFileTitle(title)
if pageNum == None:
print "URL已失效,请重试"
return
try:
print "该帖子共有" + str(pageNum) + "页"
for i in range(1,int(pageNum)+1):
print "正在写入第" + str(i) + "页数据"
page = self.getPage(i)
contents = self.getContent(page)
self.writeData(contents)
#出现写入异常
except IOError,e:
print "写入异常,原因" + e.message
finally:
print "写入任务完成"
print u"请输入帖子代号"
baseURL = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))
seeLZ = raw_input("是否只获取楼主发言,是输入1,否输入0\\n")
floorTag = raw_input("是否写入楼层信息,是输入1,否输入0\\n")
bdtb = BDTB(baseURL,seeLZ,floorTag)
bdtb.start()
python3.6下:
# -*- coding:utf-8 -*-
import urllib.error
import urllib.parse
import urllib.request
import re
#处理页面标签类
class Tool:
#去除img标签,7位长空格
removeImg = re.compile('<img.*?>| 7|')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\\t
replaceTD= re.compile('<td>')
#把段落开头换为\\n加空两格
replacePara = re.compile('<p.*?>')
#将换行符或双换行符替换为\\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
def replace(self,x):
x = re.sub(self.removeImg,"",x)
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\\n",x)
x = re.sub(self.replaceTD,"\\t",x)
x = re.sub(self.replacePara,"\\n ",x)
x = re.sub(self.replaceBR,"\\n",x)
x = re.sub(self.removeExtraTag,"",x)
#strip()将前后多余内容删除
return x.strip()
#百度贴吧爬虫类
class BDTB:
#初始化,传入基地址,是否只看楼主的参数
def __init__(self,baseUrl,seeLZ,floorTag):
#base链接地址
self.baseURL = baseUrl
#是否只看楼主
self.seeLZ = '?see_lz='+str(seeLZ)
#HTML标签剔除工具类对象
self.tool = Tool()
#全局file变量,文件写入操作对象
self.file = None
#楼层标号,初始为1
self.floor = 1
#默认的标题,如果没有成功获取到标题的话则会用这个标题
self.defaultTitle = u"百度贴吧"
#是否写入楼分隔符的标记
self.floorTag = floorTag
#传入页码,获取该页帖子的代码
def getPage(self,pageNum):
try:
#构建URL
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib.request.Request(url)
response = urllib.request.urlopen(request)
#返回UTF-8格式编码内容
return response.read().decode('utf-8')
#无法连接,报错
except urllib.error.URLError as e:
if hasattr(e,"reason"):
print(u"连接百度贴吧失败,错误原因",e.reason)
return None
#获取帖子标题
def getTitle(self,page):
#得到标题的正则表达式
pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
result = re.search(pattern,page)
if result:
#如果存在,则返回标题
return result.group(1).strip()
else:
return None
#获取帖子一共有多少页
def getPageNum(self,page):
#获取帖子页数的正则表达式
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None
#获取每一层楼的内容,传入页面内容
def getContent(self,page):
#匹配所有楼层的内容
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
contents = []
for item in items:
#将文本进行去除标签处理,同时在前后加入换行符
content = "\\n"+self.tool.replace(item)+"\\n"
contents.append(content.encode('utf-8'))
return contents
def setFileTitle(self,title):
#如果标题不是为None,即成功获取到标题
if title is not None:
self.file = open(title + ".txt","w+")
else:
self.file = open(self.defaultTitle + ".txt","w+")
def writeData(self,contents):
#向文件写入每一楼的信息
for item in contents:
if self.floorTag == '1':
#楼之间的分隔符
floorLine = "\\n" + str(self.floor) + u"-----------------------------------------------------------------------------------------\\n"
self.file.write(floorLine)
self.file.write(str(item,'utf-8'))
self.floor += 1
print(str(item,'utf-8'))
def start(self):
indexPage = self.getPage(1)
pageNum = self.getPageNum(indexPage)
title = self.getTitle(indexPage)
self.setFileTitle(title)
if pageNum == None:
print("URL已失效,请重试")
return
try:
print("该帖子共有" + str(pageNum) + "页")
for i in range(1,int(pageNum)+1):
print("正在写入第" + str(i) + "页数据")
page = self.getPage(i)
contents = self.getContent(page)
self.writeData(contents)
#出现写入异常
except IOError as e:
print("写入异常,原因" + e.message)
finally:
print("写入任务完成")
print("请输入帖子代号")
baseURL = 'http://tieba.baidu.com/p/' + str(input(u'http://tieba.baidu.com/p/'))
seeLZ = input("是否只获取楼主发言,是输入1,否输入0\\n")
floorTag = input("是否写入楼层信息,是输入1,否输入0\\n")
bdtb = BDTB(baseURL,seeLZ,floorTag)
bdtb.start()
六、爬取西刺免费代理服务器IP等信息
在使用python爬取网络数据时,网络需要设置代理服务器,防止目标网站对IP的限制。
http://www.xicidaili.com/
网站实现了众多可以使用的代理服务器,这里写了一个爬虫,爬取其中 国内高匿代理IP。
使用python3.6环境:
#coding:utf-8
#本实例用于获取国内高匿免费代理服务器
import urllib
from bs4 import BeautifulSoup
def getdata(html): #从字符串中安装正则表达式获取值
allproxy = [] # 所有的代理服务器信息
soup = BeautifulSoup(html, 'html.parser')
alltr = soup.find_all("tr", class_="")[1:] #获取tr ,第一个是标题栏,去除
for tr in alltr:
alltd =tr.find_all('td')
oneproxy =
'IP地址':alltd[1].get_text(),
'端口号': alltd[2].get_text(),
# '服务器地址': alltd[3].a.get_text(),
'是否匿名': alltd[4].get_text(),
'类型': alltd[5].get_text(),
'速度': alltd[6].div['title'],
'连接时间': alltd[7].div['title'],
'存活时间': alltd[8].get_text(),
'验证时间': alltd[9].get_text(),
allproxy.append(oneproxy)
alltr = soup.find_all("tr", class_="odd")[1:] # 获取tr ,第一个是标题栏,去除
for tr in alltr:
alltd = tr.find_all('td')
oneproxy =
'IP地址': alltd[1].get_text(),
'端口号': alltd[2].get_text(),
# '服务器地址': alltd[3].a.get_text(),
'是否匿名': alltd[4].get_text(),
'类型': alltd[5].get_text(),
'速度': alltd[6].div['title'],
'连接时间': alltd[7].div['title'],
'存活时间': alltd[8].get_text(),
'验证时间': alltd[9].get_text(),
allproxy.append(oneproxy)
return allproxy
#根据一个网址,获取该网址中符合指定正则表达式的内容
def getallproxy(url='http://www.xicidaili.com/nn'):
try:
# 构造 Request headers
agent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER'
headers = # 这个http头,根据审查元素,监听发包,可以查看
"Host": "www.xicidaili.com",
"Referer": "http://www.xicidaili.com/",
'User-Agent': agent
request = urllib.request.Request(url, headers=headers) # 创建一个请求
response = urllib.request.urlopen(request) # 获取响应
html = response.read() #读取返回html源码
return getdata(html)
except urllib.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)
return []
# allproxy = getallproxy()
# print(allproxy)
七、根据网址爬取中文网站
获取标题、子连接、子连接数目、连接描述、中文分词列表。
其中使用到了urllib、BeautifulSoup爬虫和结巴中文分词的相关知识。
调试环境python3.6:
# 根据连接爬取中文网站,获取标题、子连接、子连接数目、连接描述、中文分词列表,
import urllib
from bs4 import BeautifulSoup
import bs4
import jieba #对中文进行分词
# 分词时忽略下列词
ignorewords=[',','。','?','“','”','!',';',':','\\n','、','-',',','.','?','\\r\\n','_',' ']
# 定义爬虫类。获取链接的题目、描述、分词、深度
class crawler:
def __init__(self,url):
self.url = url
self.urls=
self.urls[url]=
'num':1, #连接被引用的次数
'title':'', #连接的标题
'text':'', #连接的描述
'allword':[], #连接的所有分词列表
def getword(self,soup):
# 获取每个单词
text=self.gettextonly(soup) #提取所有显示出来的文本
words=self.separatewords(text) #使用分词器进行分词
allword=[]
for word in words:
if word not in ignorewords:
allword.append(word)
# print(allword)
return allword
# 根据一个网页源代码提取文字(不带标签的)。由外至内获取文本元素。style和script内不要
def gettextonly(self,soup):
v=soup.string
if v==None:
c=soup.contents # 直接子节点的列表,将<tag>所有儿子节点存入列表
resulttext=''
for t in c:
if t.name=='style' or t.name=='script': #当元素为style和script和None时不获取内容
continue
subtext=self.gettextonly(t)
resulttext+=subtext+'\\n'
return resulttext
else:
if isinstance(v,bs4.element.Comment): #代码中的注释不获取
return ''
return v.strip()
# 利用正则表达式提取单词(不能区分中文)。会将任何非字母非数字字符看做分隔符
def separatewords(self,text):
seg_list = jieba.cut(text, cut_all=False) #使用结巴进行中文分词
return seg_list
# splitter=re.compile('\\\\W*')
# return [s.lower() for s in splitter.split(text) if s!='']
#爬虫主函数
def crawl(self):
try:
response=urllib.request.urlopen(self.url)
except:
print("Could not open %s" % self.url)
return
try:
text = str(response.read(),encoding='utf-8')
soup=BeautifulSoup(text,'html.parser')
title = soup.title
self.urls[self.url]['title'] = title.get_text() # 将标题加入到属性中
links=soup('a')
for link in links:
if ('href' in dict(link.attrs)):
newurl=urllib.parse.urljoin(self.url,link['href'])
if newurl.find("'")!=-1: continue
newurl=newurl.split('#')[0] # 去掉位置部分
if newurl[0:4]=='http':
if newurl not in self.urls:
linkText = self.gettextonly(link) #获取连接的描述
self.urls[newurl]=
'num':1, #连接被引用的次数
'text':linkText #链接描述
else:
self.urls[newurl]['num']+=1 #连接数+1,这里有算法只算一次
allword = self.getword(soup.body) # 获取分词
self.urls[self.url]['allword'] = allword # 将分词加入到属性中
except:
print("Could not parse page %s" % self.url)
if __name__ == '__main__':
url='http://blog.csdn.net/luanpeng825485697/article/details/78378653'
mycrawler = crawler(url)
mycrawler.crawl()
print(mycrawler.urls[url]['allword'])
八、爬取百度新闻RSS数据
各RSS网站参考http://blog.csdn.net/luanpeng825485697/article/details/78737510
今天我们使用python3.6来爬去百度新闻RSS中的数据,有了数据集,这样可以对样本数据集进行分类、聚类、推荐算法的学习
调试环境python3.6:
# 获取百度新闻数据集
import urllib
import re
from bs4 import BeautifulSoup
import json
import io
feedlist=[
'http://news.baidu.com/n?cmd=1&class=civilnews&tn=rss&sub=0', #国内焦点
'http://news.baidu.com/n?cmd=1&class=shizheng&tn=rss&sub=0', #时政焦点
'http://news.baidu.com/n?cmd=1&class=gangaotai&tn=rss&sub=0', #港澳台焦点
'http://news.baidu.com/n?cmd=1&class=internews&tn=rss&sub=0', #国际焦点
'http://news.baidu.com/n?cmd=1&class=mil&tn=rss&sub=0', #军事焦点
'http://news.baidu.com/n?cmd=1&class=hqsy&tn=rss&sub=0', #环球视野焦点
'http://news.baidu.com/n?cmd=1&class=finannews&tn=rss&sub=0', #财经焦点
'http://news.baidu.com/n?cmd=1&class=stock&tn=rss&sub=0', #股票焦点
'http://news.baidu.com/n?cmd=1&class=money&tn=rss&sub=0', #理财焦点
'http://news.baidu.com/n?cmd=1&class=financialnews&tn=rss&sub=0', #金融观察焦点
'http://news.baidu.com/n?cmd=1&class=internet&tn=rss&sub=0', #互联网焦点
'http://news.baidu.com/n?cmd=1&class=rwdt&tn=rss&sub=0', #人物动态焦点
'http://news.baidu.com/n?cmd=1&class=gsdt&tn=rss&sub=0', #公司动态焦点
'http://news.baidu.com/n?cmd=1&class=housenews&tn=rss&sub=0', #房产焦点
'http://news.baidu.com/n?cmd=1&class=gddt&tn=rss&sub=0', #各地动态焦点
'http://news.baidu.com/n?cmd=1&class=zcfx&tn=rss&sub=0', #政策风向焦点
'http://news.baidu.com/n?cmd=1&class=fitment&tn=rss&sub=0', #家居焦点
'http://news.baidu.com/n?cmd=1&class=autonews&tn=rss&sub=0', #汽车焦点
'http://news.baidu.com/n?cmd=1&class=autobuy&tn=rss&sub=0', #新车导购焦点
'http://news.baidu.com/n?cmd=1&class=autoreview&tn=rss&sub=0', #试驾焦点
'http://news.baidu.com/n?cmd=1&class=sportnews&tn=rss&sub=0', #体育焦点
'http://news.baidu.com/n?cmd=1&class=nba&tn=rss&sub=0', #NBA焦点
'http://news.baidu.com/n?cmd=1&class=worldsoccer&tn=rss&sub=0', #国际足球焦点
'http://news.baidu.com/n?cmd=1&class=chinasoccer&tn=rss&sub=0', #国内足球焦点
'http://news.baidu.com/n?cmd=1&class=cba&tn=rss&sub=0', #国内篮球焦点
'http://news.baidu.com/n?cmd=1&class=othersports&tn=rss&sub=0', #综合体育焦点
'http://news.baidu.com/n?cmd=1&class=olympic&tn=rss&sub=0', #奥运焦点
'http://news.baidu.com/n?cmd=1&class=enternews&tn=rss&sub=0', #娱乐焦点
'http://news.baidu.com/n?cmd=1&class=star&tn=rss&sub=0', #明星焦点
'http://news.baidu.com/n?cmd=1&class=film&tn=rss&sub=0', #电影焦点
'http://news.baidu.com/n?cmd=1&class=tv&tn=rss&sub=0', #电视焦点
'http://news.baidu.com/n?cmd=1&class=music&tn=rss&sub=0', #音乐焦点
'http://news.baidu.com/n?cmd=1&class=gamenews&tn=rss&sub=0', #游戏焦点
'http://news.baidu.com/n?cmd=1&class=netgames&tn=rss&sub=0', #网络游戏焦点
'http://news.baidu.com/n?cmd=1&class=tvgames&tn=rss&sub=0', #电视游戏焦点
'http://news.baidu.com/n?cmd=1&class=edunews&tn=rss&sub=0', #教育焦点
'http://news.baidu.com/n?cmd=1&class=exams&tn=rss&sub=0', #考试焦点
'http://news.baidu.com/n?cmd=1&class=abroad&tn=rss&sub=0', #留学焦点
'http://news.baidu.com/n?cmd=1&class=healthnews&tn=rss&sub=0', #健康焦点
'http://news.baidu.com/n?cmd=1&class=baojian&tn=rss&sub=0', #保健养生焦点
'http://news.baidu.com/n?cmd=1&class=yiyao&tn=rss&sub=0', #寻医问药焦点
'http://news.baidu.com/n?cmd=1&class=technnews&tn=rss&sub=0', #科技焦点
'http://news.baidu.com/n?cmd=1&class=mobile&tn=rss&sub=0', #手机焦点
'http://news.baidu.com/n?cmd=1&class=digi&tn=rss&sub=0', #数码焦点
'http://news.baidu.com/n?cmd=1&class=computer&tn=rss&sub=0', #电脑焦点
'http://news.baidu.com/n?cmd=1&class=discovery&tn=rss&sub=0', #科普焦点
'http://news.baidu.com/n?cmd=1&class=socianews&tn=rss&sub=0', #社会焦点
'http://news.baidu.com/n?cmd=1&class=shyf&tn=rss&sub=0', #社会与法焦点
'http://news.baidu.com/n?cmd=1&class=shwx&tn=rss&sub=0', #社会万象焦点
'http://news.baidu.com/n?cmd=1&class=zqsk&tn=rss&sub=0', #真情时刻焦点
]
def getrss1(feedlist):
for url in feedlist:
info=
info[url]=
'title':'',
'allitem':[]
try:
response=urllib.request.urlopen(url)
text = str(response.read(), encoding='utf-8')
soup = BeautifulSoup(text, 'lxml')
title = soup.title
info[url]['title']=title
for item in soup('item'):
try:
print(item)
suburl=
'title':item('title').replace(']]>','').replace('<![CDATA[',''),
'link': item('link').replace(']]>', '').replace('<![CDATA[', ''),
'source': item('source').replace(']]>', '').replace('<![CDATA[', ''),
'text': item('description').get_text().replace(']]>',''),
'type':title
print(suburl)
info[url]['allitem'].append(suburl)
except:
print('无法匹配'+item)
except:
print("error: %s" % url)
def getrss(feedlist):
rss =
for url in feedlist:
rss[url] =
'title': '',
'allitem': []
try:
response = urllib.request.urlopen(url)
text = str(response.read(), encoding='utf-8')
soup = BeautifulSoup(text, 'lxml')
title = soup.title.get_text()
rss[url]['title'] = title
patterstr = r'<item>.*?' \\
r'<title>(.*?)</title>.*?' \\
r'<link>(.*?)</link>.*?' \\
r'<source>(.*?)</source>.*?' \\
r'<description>.*?<br>(.*?)<br.*?' \\
r'</item>'
pattern = re.compile(patterstr,re.S) #使用多行模式
results = re.findall(pattern, text) #如何查询多次
if results!=None or len(results)==0:
for result in results:
suburl =
'title': result[0].replace(']]>', '').replace('<![CDATA[', ''),
'link': result[1].replace(']]>', '').replace('<![CDATA[', ''),
'source': result[2].replace(']]>', '').replace('<![CDATA[', ''),
'text': result[3].replace(']]>', ''),
'type': title
print(suburl)
rss[url]['allitem'].append(suburl)
except:
print("error: %s" % url)
return rss
# 形成一个文本描述和分类的数据集。
if __name__ == '__main__':
rss = getrss(feedlist)
jsonstr = json.dumps(rss,ensure_ascii=False)
f = io.open('rss.json', 'w', encoding='utf-8')
f.writelines(jsonstr)
f.close()
九、多线程+队列:爬取某网站所有图片
实现思路:
- download_image(url, image_dir, image_no):将图片下载页的主图下载到本地。
- get_image_url(url):拼接图片下载的 url(绝对路径)。由于网站中的图片 src 都是相对路径,因此需要在此函数中拼接图片的绝对路径。
- get_page_url(url):获取图片浏览页中的图片下载页 url 和翻页 url。
- task(queue):多线程的任务函数,调用上述 3 个函数。
代码:
下载地址:https://github.com/juno3550/Crawler
import requests
import re
import os
import threading
import queue # Python的queue模块中提供了同步的、线程安全的队列类
from bs4 import BeautifulSoup
import traceback
# 队列,用来存放图片下载页url和翻页url
queue = queue.Queue()
# 种子url
result_urls = []
# 图片命名的起始编号
image_no = 0
# 图片存储路径
image_dir = "e:\\\\crawl_image"
if not os.path.exists(image_dir):
os.makedirs(image_dir)
# 将图片下载到本地
def download_image(url, image_dir, image_no):
try:
# 访问图片并设置超时时间
r = requests.get(url, timeout=60)
# 获取图片的后缀名
image_ext = url.split(".")[-1]
# 设置下载路径与图片名称
image_name = str(image_no) + "." + image_ext
image_path = os.path.join(image_dir, image_name)
# 保存图片到本地
with open(image_path, "wb") as f:
f.write(r.content)
print("图片【%s】下载成功,保存路径【%s】" % (url, image_path))
except:
print("图片下载失败【%s】" % url)
traceback.print_exc()
# 获取图片下载url(绝对路径)
def get_image_url(url):
# 由于网站中的图片url都是相对路径,因此需要在此函数中拼接图片的绝对路径
# 获取网站首页链接
try:
home_page = re.match(r"http[s]?://\\w+.\\w+\\.com", url).group()
r = requests.get(url, timeout=60)
r.encoding = "gbk"
# 通过a标签获取其中的src下载路径
# 通过BeautifulSoup解析网页内容
soup = BeautifulSoup(r.text, "html.parser")
image_a = soup.find_all("a", attrs="id": "img") # 找出id属性值为img的a标签,即主图
if image_a:
# 获得图片的相对路径
image_relative_url = re.search(r'src="(.+?)"', str(image_a[0])).group(1)
# 拼接绝对路径
image_abs_url = home_page + image_relative_url
return image_abs_url
except:
print("获取图片下载url失败【%s】" % url)
traceback.print_exc()
# 获取图片浏Python爬虫开发第1篇爬虫案例
案例一:网站模拟登录
# douban.py
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.PhantomJS()
driver.get("http://www.douban.com")
# 输入账号密码
driver.find_element_by_name("form_email").send_keys("[email protected]")
driver.find_element_by_name("form_password").send_keys("xxxxxxxx")
# 模拟点击登录
driver.find_element_by_xpath("//input[@class=‘bn-submit‘]").click()
# 等待3秒
time.sleep(3)
# 生成登陆后快照
driver.save_screenshot("douban.png")
with open("douban.html", "w") as file:
file.write(driver.page_source)
driver.quit()
案例二:动态页面模拟点击
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# python的测试模块
import unittest
from selenium import webdriver
from bs4 import BeautifulSoup
class douyuSelenium(unittest.TestCase):
# 初始化方法
def setUp(self):
self.driver = webdriver.PhantomJS()
#具体的测试用例方法,一定要以test开头
def testDouyu(self):
self.driver.get(‘http://www.douyu.com/directory/all‘)
while True:
# 指定xml解析
soup = BeautifulSoup(driver.page_source, ‘xml‘)
# 返回当前页面所有房间标题列表 和 观众人数列表
titles = soup.find_all(‘h3‘, {‘class‘: ‘ellipsis‘})
nums = soup.find_all(‘span‘, {‘class‘: ‘dy-num fr‘})
# 使用zip()函数来可以把列表合并,并创建一个元组对的列表[(1,2), (3,4)]
for title, num in zip(nums, titles):
print u"观众人数:" + num.get_text().strip(), u" 房间标题: " + title.get_text().strip()
# page_source.find()未找到内容则返回-1
if driver.page_source.find(‘shark-pager-disable-next‘) != -1:
break
# 模拟下一页点击
self.driver.find_element_by_class_name(‘shark-pager-next‘).click()
# 退出时的清理方法
def tearDown(self):
print ‘加载完成...‘
self.driver.quit()
if __name__ == "__main__":
unittest.main()
案例三:执行 JavaScript 语句
1.隐藏百度图片
from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get("https://www.baidu.com/")
# 给搜索输入框标红的javascript脚本
js = "var q=document.getElementById("kw");q.style.border="2px solid red";"
# 调用给搜索输入框标红js脚本
driver.execute_script(js)
#查看页面快照
driver.save_screenshot("redbaidu.png")
#js隐藏元素,将获取的图片元素隐藏
img = driver.find_element_by_xpath("//*[@id=‘lg‘]/img")
driver.execute_script(‘$(arguments[0]).fadeOut()‘,img)
# 向下滚动到页面底部
driver.execute_script("$(‘.scroll_top‘).click(function(){$(‘html,body‘).animate({scrollTop: ‘0px‘}, 800);});")
#查看页面快照
driver.save_screenshot("nullbaidu.png")
driver.quit()
2.模拟滚动条滚动到底部
from selenium import webdriver
import time
driver = webdriver.PhantomJS()
driver.get("https://movie.douban.com/typerank?type_name=剧情&type=11&interval_id=100:90&action=")
# 向下滚动10000像素
js = "document.body.scrollTop=10000"
#js="var q=document.documentElement.scrollTop=10000"
time.sleep(3)
#查看页面快照
driver.save_screenshot("douban.png")
# 执行JS语句
driver.execute_script(js)
time.sleep(10)
#查看页面快照
driver.save_screenshot("newdouban.png")
driver.quit()
以上是关于Python 爬虫案例的主要内容,如果未能解决你的问题,请参考以下文章