Python3 爬虫

一.  操作系统

操作系统: Windows 或 Linux均可,安装好Python运行环境,加入环境变量即可。

很Linux发行版默认为Python2,需要再安装Python3,步骤:

以CentOS7 下安装 Python3.7为例:

1. 安装依赖包
#yum install zlib* libffi-devel -y

2. 下载 Python-3.7.0
#wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz
#tar -zxf Python-3.7.0.tgz
#cd Python-3.7.0/

3. 安装 gcc
#yum insatll -y gcc

4. 编译,执行
#./configure --prefix=/usr/local/python3
#make
#make install

5. 生成软链接
#ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3
#ln -s /usr/local/python3/bin/python3  /usr/bin/python3/

二.  正则表达式

学习爬虫需要懂得简单的正则表达式的写法:
教程:
零基础写python爬虫之神器正则表达式 >>

三.  第三方库

1. BeautifulSoup

BeautifulSoup是Python的一个库,最主要的功能就是从网页爬取我们需要的数据。BeautifulSoup将html解析为对象进行处理,全部页面转变为字典或者数组,相对于正则表达式的方式,可以大大简化处理过程。
安装

#pip3  install --upgrade beautifulsoup4

BeautifulSoup测试代码:

#filename: bs4_test.py
from bs4 import BeautifulSoup
html = '''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''
soup = BeautifulSoup(html,'html.parser') #soup = BeautifulSoup(html, 'lxml')
print(soup.prettify())
print(soup.title)
print(soup.title.name)
print(soup.title.string)
print(soup.title.parent.name)
print(soup.p)
print(soup.p["class"])
print(soup.a)
print(soup.find_all('a'))
print(soup.find(id='link3'))

2. lxml
第三方xml解析工具
安装

#pip3  install lxml

四.  写爬虫步骤

1. 要明确爬取的网站结构。
(1)url
爬虫主要解析页面的url, 以及每个url页面的html标签(如head,body, div, p, a等)。首先对所要爬取的页面 url和内容的布局非常了解。

比如爬取凤凰网的所有新闻。 凤凰网大部分新闻正文的页面链接,都是这种形式:

http://news.ifeng.com/a/20170623/51308599_0.shtml

可以用正则表达式把这些页面从新闻首页中提取出来

re_newsdetail = 'http://news\.ifeng\.com\/a\/[0-9]{8}\/[0-9]{8}\_0\.shtml$'

(2)页面中的标签元素
我们主要提取的是新闻的标题和正文内容。 可以在浏览器(chrome)里用F12, 来观察页面布局。
比如凤凰网新闻正文页面, 标题都在<div id='artical'>中,是<h1>标签, 正文都在<div id='main_content'>里, 是<p>标签。

3. 编写程序并调试
urllib负责对url的处理,主要是request, parse, error, 这几个模块。 负责对url发送请求,获取页面信息,error的处理。
Beautifulsoup负责对页面的解析,获取的页面是html树形结构, 通过findAll(), select(), get(), get_text()等函数即可很方便的获取到我们想要的内容。

4.最后,如果是想要获取整个门户网站的数据,则需要一些递归, 整个网站相当于一个图结构, dfs(深度优先遍历)是比较好的方法。

多级递归爬虫示例:

#coding: utf-8
 
import codecs
from urllib import request, parse
from bs4 import BeautifulSoup
import re
import time
from urllib.error import HTTPError, URLError
import sys
 
###新闻类定义
class News(object):
  def __init__(self):
    self.url = None  #该新闻对应的url
    self.topic = None #新闻标题
    self.date = None #新闻发布日期
    self.content = None  #新闻的正文内容
    self.author = None  #新闻作者
 
###如果url符合解析要求,则对该页面进行信息提取
def getNews(url):
  #获取页面所有元素
  html = request.urlopen(url).read().decode('utf-8', 'ignore')
  #解析
  soup = BeautifulSoup(html, 'lxml')
 
  #检查是否是 artical
  if not(soup.find('div', {'id':'artical'})): return 
  
  news = News()  #建立新闻对象
 
  page = soup.find('div', {'id':'artical'}) #正文及标题
  
  if not(page.find('h1', {'id':'artical_topic'})): return
  #新闻标题
  topic = page.find('h1', {'id':'artical_topic'}).get_text()   
  news.topic = topic
 
  if not(page.find('div', {'id': 'main_content'})): return 
  #新闻正文内容
  main_content = page.find('div', {'id': 'main_content'})   
  
  content = ''
  
  #新闻内容 
  for p in main_content.select('p'): 
    content = content + p.get_text() 
    news.content = content news.url = url #新闻页面对应的url 
  
  f.write(news.topic+'\t'+news.content+'\n') 

##dfs算法遍历全站### 
def dfs(url): 
  #global count 
  print(url) 
  #可以继续访问的url规则 
  pattern1 = 'http://news\.ifeng\.com\/[a-z0-9_\/\.]*$' 
  #解析新闻信息的url规则 
  pattern2 = 'http://news\.ifeng\.com\/a\/[0-9]{8}\/[0-9]{8}\_0\.shtml$' 
  #该url访问过,则直接返回 

  if url in visited: return print(url) 
    #把该url添加进visited() 
    visited.add(url) 

  try: #该url没有访问过的话,则继续解析操作 
    html = request.urlopen(url).read().decode('utf-8', 'ignore') 
    
    ####提取该页面其中所有的url#### 
    links = soup.findAll('a', href=re.compile(pattern1)) 

  for link in links: 
    print(link['href']) 
    if link['href'] not in visited: 
      dfs(link['href']) #递归调用 

  except URLError as e: 
    print(e) 
    return 
  except HTTPError as e: 
    print(e) 
    return 
    
visited = set() ##存储访问过的url 
f = open('news.txt', 'a+', encoding='utf-8') 

dfs('http://news.ifeng.com/')

两级爬虫及简单的数据库操作示例:

#coding: utf-8
 
import codecs
from urllib import request, parse
from bs4 import BeautifulSoup
import re
import time
from urllib.error import HTTPError, URLError
import sys
import json
import pymysql

# 打开数据库连接
connect = pymysql.Connect(
  host='localhost',
  port=3306,
  user='root',
  passwd='yourpwd',
  db='yourdb',
  charset='utf8'
)

# 使用cursor()方法获取操作游标 
cursor = connect.cursor()

# sql语句前半段
sql = "INSERT INTO article(title,author,content,cover,description," \
  "publishedtime,cid) VALUES "

# 提取新闻内容
def getDetails(url):

  #获取页面所有元素
  html = request.urlopen(url).read().decode('utf-8', 'ignore')

  #解析
  soup = BeautifulSoup(html, 'lxml')

  #获取信息
  if not(soup.find('div', {'id':'artical'})): return 
 
  page = soup.find('div', {'id':'artical'})

  #print('*** analysis page start ***')
  
  if not(page.find('h1', {'id':'artical_topic'})): return
   #新闻标题 
  topic = page.find('h1', {'id':'artical_topic'}).get_text()   
 
  if not(page.find('div', {'id': 'main_content'})): return 
  #新闻正文内容
  main_content = page.find('div', {'id': 'main_content'})   
  
  content = ''
  if main_content.find('br'):
    content = main_content.prettify()
  else:
    for p in main_content.select('p'):
      if p.get_text() == '': continue
      content += "<p>" +p.get_text()+"</p>" 
      content = content.replace("\'","") 

  #每个新闻内容页的js脚本中有一个json格式的新闻简介 
  #可以提取cover图片,新闻标题,摘要,类别等 
  for sc in soup.findAll("script"): 
    if '@TODO: 全局参数' in sc.prettify(): 
      try: 
        detail = sc.prettify() 
        detail = detail.replace("\'","\"") 
        jsonStart = findStr(detail, '{', 2) 
        jsonEnd = detail.index('}') 
        detail = detail[jsonStart:jsonEnd] 
        jsonData = json.loads(detail+'}') 
      except: 
        return 
  author = '凤凰网' 
  title = topic 
  content = content 
  desc = jsonData['summary'] 
  publishedtime = str(int(time.time())) 
  cid = '1' 
  cover = jsonData['image'] 

  strSql.add(sql+"('"+title+"', '"+author+"','"+content+"','"+ \ 
    cover+"','"+desc+"',"+publishedtime+","+cid+")") 

# 遍历新闻主页面 
def getNews(url): 
  global count 
  #解析新闻信息的url规则
  pattern = 'http://news\.ifeng\.com\/a\/[0-9]{8}\/[0-9]{8}\_0\.shtml$' 
  try: 
    html = request.urlopen(url).read().decode('utf-8', 'ignore') 
    soup = BeautifulSoup(html, 'lxml') 
  
    # 提取该页面其中所有的url 
    links = soup.findAll('a', href=re.compile(pattern)) 
  
    count = 1 

    for link in links: 
      if (count <= 30) : 
        print(link['href']+' **** count: '+str(count)) 
        getDetails(link['href']) 
        count += 1 
      else: break 

  except URLError as e: 
    print(e) 
    return 
  except HTTPError as e: 
    print(e) 
    return 

#查找第N(findCnt)次出现的位置 
def findStr(string, subStr, findCnt): 
  listStr = string.split(subStr,findCnt) 
  if len(listStr) <= findCnt: 
    return -1 
  return len(string)-len(listStr[-1])-len(subStr) 

############################################################## 
strSql = set() 
today = time.strftime('%Y%m%d',time.localtime()) 
#getDetails('http://news.ifeng.com/a/20180604/58574816_0.shtml') 

try: 
  getNews('http://news.ifeng.com/listpage/11502/'+today+'/1/rtlist.shtml') 

  for strsql in strSql: 
    # 执行sql语句
    cursor.execute(strsql) 
  # 提交到数据库执行 
  connect.commit()
  print('成功插入', len(strSql), '条数据') 
  
  cursor.close() 

except Exception as e: 
  cursor.close() 
  # 如果发生错误则回滚 
  connect.rollback() 
  print('事务处理失败', e) 
  # 关闭数据库连接 
  connect.close()

爬虫效果展示:

  

多级爬虫参考:https://blog.csdn.net/MrWilliamVs/article/details/76422584?locationNum=9&fps=1ss


Comments are closed.