1. 程式人生 > >java呼叫Linux執行Python爬蟲,並將資料儲存到elasticsearch--(一、環境指令碼搭建)

java呼叫Linux執行Python爬蟲,並將資料儲存到elasticsearch--(一、環境指令碼搭建)

java呼叫Linux執行Python爬蟲,並將資料儲存到elasticsearch中

一、以下部落格程式碼使用的開發工具及環境如下:

1、idea:

2、jdk:1.8

3、elasticsearch:5.2.0

4、Linux

5、Python

6、maven

二、maven座標:

<!--java連線ulinix指令碼架包-->
        <dependency>
            <groupId>ch.ethz.ganymed</groupId>
            <artifactId>ganymed-ssh2</artifactId>
            <version>build209</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
<!--es相關座標-->

<dependency>
    <groupId>org.elasticsearch.plugin</groupId>
    <artifactId>transport-netty4-client</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId

>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.nlpcn</groupId>
    <artifactId>elasticsearch-sql</artifactId>
    <version>6.3.0.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId
>druid</artifactId>
    <version>1.1.9</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>5.2.0</version>
</dependency>

 二、Linux指令碼

cd /usr/local/python3/lib/python3.6/site-packages
python linux_sina.py &

三、Python爬蟲指令碼

#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Created on Mon Aug 13 10:12:56 2018

@author: Administrator
"""

import public_python as p
import urllib.request
from bs4 import BeautifulSoup
#儲存到ES
from elasticsearch import Elasticsearch

import flask
from flask import request
server = flask.Flask(__name__)

from selenium import webdriver


@server.route('/news',methods=['get','post'])
def get_html2():
    #異常處理機制:
    #宣告全域性變數
    #global title,time,strcon,cos
    try:
        url=request.values.get("url")
        #儲存的新聞型別----用於資料庫表中的欄位
        theme_id=request.values.get("theme_id")#----guonei--新聞型別 
        
        #無介面啟動
        firefox_options = webdriver.FirefoxOptions()
        firefox_options.add_argument('--headless')
        browser = webdriver.Firefox(firefox_options=firefox_options)
        #超時設定
        #timeout=request.values.get("timeout")
        '''設定載入時間'''
        browser.set_page_load_timeout(30000)
        browser.set_script_timeout(30000)#這兩種設定都進行才有效
        browser.get(url)
        '''
        判斷url是否包含多個連結,如果包含--即URL值不會是以.shtml結尾的字串
        那麼就去獲取連結裡面所有的以.shtml結尾的連結url值。
        '''
        #存放頁面中具體的url連結
        data_urls=[]
        if ".shtml" not in url:

            list_father=browser.find_element_by_tag_name('body')#先定位大的標籤
            #存放所有<a>連結
            ahref=list_father.find_elements_by_tag_name("a")#在打標籤下尋找確定的小標籤的集合-需要:elements。
              
            '''
            對標籤<a>進行遍歷,獲取其中的屬性為:href的值,
            然後過濾判斷,如果包含.shtml,那麼將href的值儲存到陣列,並且對陣列進行set去重。
            如果沒包含.shtml,那麼跳過本次迴圈。
            最後呼叫獲取資料的方法進行爬取資料
            '''
            for ah in ahref:#具體小標籤中包含內容較多,所以還需要遍歷 
                         
                hr=ah.get_attribute("href")#獲取連線的值
                if ".shtml" in hr: #對連結進行遍歷過濾
                      data_urls.append(hr)
                      links=set(data_urls)#去重
                else:
                      pass
                      continue
            #getcontent(links)#呼叫獲取內容的方法:
        '''
         判斷url是否包含多個連結,如果url包含。shtml,那麼代表此url是單頁的。
         那麼直接將url存入陣列,然後呼叫獲取資料的方法
        '''
        if ".shtml"  in url:
            data_urls.append(url)
            links=set(data_urls)#去重
            #getcontent(links)
        '''
        #最後均去呼叫獲取內容的方法:
        '''
        getcontent(links)
        return "ok"
        
        #測試 
        #data_urls中包含網頁中所有.shtml的連結。 
        '''
        for i in range(len(data_urls)):
              print(data_urls[i])
        '''

        browser.quit()#完成後退出關閉瀏覽器
    except urllib.error.URLError as e :
        if hasattr(e,"code"):
            print(e.code)
            return e.code
        if hasattr(e,"reason"):
            print(e.reason)
            return e.reason
    except Exception as e:
        print("exception:"+str(e))
        return e

def getcontent(data_urls):
    #異常處理機制:
    try:
        global title,time,strcon,cos
        from selenium import webdriver
        #儲存的新聞型別----用於資料庫表中的欄位
        theme_id=request.values.get("theme_id")#----guonei--新聞型別         
        #圖片的字首連結域名
        #domain_name=request.values.get("domain_name")
        
        #無介面啟動
        firefox_options = webdriver.FirefoxOptions()
        firefox_options.add_argument('--headless')
        browser = webdriver.Firefox(firefox_options=firefox_options)
        
        '''
        連線ES叢集,獲取es
        '''
        es=Elasticsearch(hosts=['192.168.200.211:9201'], maxsize=25)
       
        '''
        對傳遞過來的url陣列進行遍歷,獲取每一個具體連結url(以.shtml結尾的url)
        '''
        for url in data_urls:
            print(url) #具體每個標題連結,字尾是.shtml

            browser.get(url)

            conlist=[] #儲存文章內容,先以陣列進行儲存
            
            #判斷原始碼中id的值:artibodyTitle是否存在,存在的話將值內容賦值給標題變數
            if "artibodyTitle" in browser.page_source:
                title=browser.find_element_by_id("artibodyTitle").text
            #判斷class的值:main-title是否存在,存在的話將值內容賦值給標題變數
            elif "main-title" in browser.page_source:
                title=browser.find_element_by_class_name("main-title").text
                '''
                以上是對新浪網新聞中標題存在的不同形式的判斷。
                '''
            else:
                #跳過異常錯誤 繼續執行
                #pass
                #continue
                title="無法爬取標題"
            print(title)
    
            '''
            以下是獲取時間的方式。
            '''
            
            if "pub_date" in browser.page_source:
                  time=browser.find_element_by_id("pub_date").text
            elif "date" in browser.page_source:
                  time=browser.find_element_by_class_name("date").text
            else:
                 #pass
                 #continue
                 time="無法爬取時間"
            #print(time)

            '''
            以下是去獲取正文內容。
            先:開啟url,並讀取內容
            '''
            
            file=urllib.request.urlopen(url=url,timeout=30000)
            data=file.read().decode('utf-8','ignore')
            soup=BeautifulSoup(data,'lxml')
            
            '''#存實際的所有正文的內容--不以陣列形式儲存'''
            strcon=""
            
            '''
            #內容有2中情況,一種在class=article的div下(class以“.”表示),
                          一種在id=artibody的div下(id以“#”表示)
            '''
            if soup.select('.article ') :
                 cos=soup.select('.article ')
            elif  soup.select('#artibody '):
                 cos=soup.select('#artibody ')
            '''
            判斷包含內容的標籤是否存在,以下程式碼均是在內容存在的情況下進行
            '''                    
            if cos:
                 for i in cos:
                      '''
                      遍歷內容標籤,查詢包含圖片和段落的標籤(img和p),
                      結果是bs4.element.ResultSet集合。
                      集合中每一個元素是一個tag標籤
                      '''
                      alls=i.find_all(["img","p"])#傳入一個字串的列表,將匹配列表中標籤的Tag全部返回
                      #print(type(alls))       #<class 'bs4.element.ResultSet'>
                      #(type(alls[0]))         #<class 'bs4.element.Tag'>
                
                 '''
                 對過濾後的標籤結合進行遍歷。
                 '''
                 for j in alls:
                      #print(j)   #---div-article標籤下所以內容。包含標籤在內
                      '''
                      #接下來需要將圖片進行替換成本地圖片:
                      #第一步:將本頁的圖片按原名下載下來
                      #第二步,替換標籤<img>中src的來源路徑即可
                      '''
                      if j in soup.findAll("img"):
                          
                           #獲取圖片的連線url
                           imgAllName=str(j.attrs["src"])
                           #圖片名稱
                           imgName=imgAllName.split("/")[-1].split(".")[0]#aaa.jpg格式--aaa
                           #圖片字尾
                           imgName_suffix=imgAllName.split("/")[-1].split(".")[-1]#類似jpg
                           #將圖片存入本地檔案-由於網址中src缺少"http:",所以需要新增形成完整url路徑
                           urllib.request.urlretrieve("http:"+imgAllName,"//opt//zc//img//"+imgName+"."+imgName_suffix)
                           '''
                           #設定新的圖片(目的:將本地圖片地址去替換原來的連結地址)-本地圖片的位置和圖片名稱連結
                           '''
                           imglink="http:/"+"/opt/zc/img/"+imgName+"."+imgName_suffix
                           
                           '''
                           #修改:圖片位置存放連結,將本地圖片地址去替換原來的連結地址
                           #j.attrs["src"]通過標籤的attrs屬性獲取裡面的屬性值
                           '''
                           j.attrs["src"]=imglink 
                           
                      
                      #此新增的就是僅僅修改圖片連結地址後全部的內容。
                      conlist.append((j))
                 '''
                 遍歷儲存內容的陣列,將其儲存為一個整體
                 '''
                 for i in range(len(conlist)):
                     strcon+=str(conlist[i]) #str:將標籤內容轉換成string型別
                 #print(strcon)#內容
                 '''   
                 #以下是ES儲存的時候的表字段
                 '''
                 #儲存的新聞型別----用於資料庫表中的欄位
                 #theme_id=request.values.get("theme_id")#----guonei--新聞型別 
                 #autoid 主鍵
                 #company_id="" # 公司ID
                 #title:標題-title
                 #content:內容-strcon
                
                 
                 '''
                 對es中的索引進行判斷,檢視是否存在
                 如果不存在,那麼就建立,將id的值賦值為1,然後新增資料
                 '''
                 if es.indices.exists(index=theme_id) is not True:
                     
                     autoid=1 #設定es中id的值
                     #將結果寫入ES
                     data={"id":autoid,"title":title,
                           "content":strcon
                           }#str(conlist)轉換成str型別 否則序列化失敗
                     #建立新索引---注意位置在此!
                     es.indices.create(index=theme_id)
                     p.insert_result(theme_id,"sina",data)

                     
                                   
                     '''
                     如果索引存在的話,先查詢出索引中所有內容,然後將資料進行轉換成dataframe
                     然後去獲取其中關於標題:title和主鍵:id的值。
                 
                     '''
                 else:
                     
                     '''
                          #去重,先去查詢ES,如果title不相同的,那麼繼續執行,否則跳出此迴圈,pass,continue
                          繼續執行的前提下,查詢ES中的id的最大值,然後每次儲存,id+1
            
                     '''  
                     
                     res0=p.search(theme_id,"sina")#查詢方法
                     
                     res1=p.clean_data(res0)#對資料進行轉換成:dataFrame
                     
                     res_title=res1[["title"]]#獲取需要的部分資料dataframe--
                     res_id=res1[["id"]]#獲取需要的部分資料dataframe
                     
                     #print(res_title.values)#---[[],[]]:二維陣列
                     
                     titles=[]#儲存es中已經存在的title的集合
                     for i in res_title.values:
                         #print(i)#[] [] []
                         for j in i:
                             titles.append(j)
                     '''
                     以上,標題titles儲存的就是一維陣列
                     '''

                     ids=[]#儲存es中已經存在的id的集合
                     for i in res_id.values:
                                 #print(i)#[] [] []
                                 for j in i:
                                     ids.append(j)
                     '''
                     以上,主鍵ids:儲存的就是一維陣列
                     '''
                   
                     '''
                     由於每次插入資料id自動增加,所以先要去檢視es中(ids)的最大值,然後每加一條記錄+1。
                     #對ids陣列進行遍歷,獲取最大值,#max(ids)是:<class 'numpy.int64'>,要轉換成int型別
                     #print(type(ids))#list型別
                     '''                  
                     autoid_max=int(max(ids)) 
                     #設定es中id的值,自動增加1
                     
                     #print(type(autoid_max))#int 型別
                     autoid=int(autoid_max+1)
                 
                     '''
                     去重處理。
                     #print(titles)#titles:['','','']
                     #對title的值進行判斷是否存在。--去重!!!!!,如果存在那麼跳出本次迴圈
                     '''
                     if title in titles:
                            #pass
                            #continue
                            print("已經存在此標題")
                            
                     else:
                             
                             data={"id":autoid,"title":title,"content":strcon
                                   }
                             #呼叫方法,往es中插入資料
                             
                             p.insert_result(theme_id,"sina",data)
    

                 #seach獲取---本地列印
                 res00=p.search(theme_id,"sina")#查詢方法
                 print(res00)
                 return "ok" 
              
    except urllib.error.URLError as e :
        if hasattr(e,"code"):
            print("==="+e.code)
            return e.code
        if hasattr(e,"reason"):
            print("----"+e.reason)
            return e.reason
    except Exception as e:
        print("exception:"+str(e))
        return e
server.run(host='0.0.0.0',port=8000,debug=True)

以上Linux指令碼及Python爬蟲指令碼,作者在此感謝我的同事張超提供。下一篇部落格作者將為大家提供後臺java程式碼。此篇部落格主要為Python爬蟲都是自己書寫的小夥伴參考