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 爬虫案例的主要内容,如果未能解决你的问题,请参考以下文章

9. 爬虫训练场,分页爬虫案例设计Demo,打通 Python Flask 和 MySQL

10. 爬虫训练场,分页爬虫案例前端页面制作

10. 爬虫训练场,分页爬虫案例前端页面制作

Python爬虫进阶——urllib模块使用案例淘宝

Python爬虫案例:利用Python爬取笑话网

Python 爬虫案例