日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网络爬虫:使用多线程爬取网页链接

發布時間:2025/3/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络爬虫:使用多线程爬取网页链接 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:

? 經過前面兩篇文章,你想大家應該已經知道網絡爬蟲是怎么一回事了。這篇文章會在之前做過的事情上做一些改進,以及說明之前的做法的不足之處。


思路分析:

1.邏輯結構圖

??

? 上圖中展示的就是我們網絡爬蟲中的整個邏輯思路(調用Python解析URL,這里只作了簡略的展示)。


2.思路說明:

? 首先,我們來把之前思路梳理一下。之前我們采用的兩個隊列Queue來保存已經訪問過和待訪問的鏈接列表,并采用廣度優先搜索進行遞歸訪問這些待訪問的鏈接地址。而且這里使用的是單線程操作。在對數據庫的操作中,我們添加了一個輔助字段cipher_address來進行“唯一”性保證,因為我們擔心MySQL在對過長的url鏈接操作時會有一些不盡如人意。

? 我不知道上面這一段能否讓你對之前我們處理Spider的做法有一個大概的了解,如果你還沒有太明白這是怎么一回事。你可以訪問《網絡爬蟲初步:從訪問網頁到數據解析》和《網絡爬蟲初步:從一個入口鏈接開始不斷抓取頁面中的網址并入庫》這兩篇文章進行了解。

? 下面我就來說明一下,之前的做法存在的問題:

? 1.單線程:采用單線程的做法,可以說相當不科學,尤其是對付這樣一個大數據的問題。所以,我們需要采用多線程來處理問題,這里會用到多線程中的線程池。


? 2.數據存儲方式:如果我們采用內存去保存數據,這樣會有一個問題,因為數據量非常大,所以程序在運行的過種中必然會內存溢出。而事實也正是如此:

??


? 3.Url去重的方式:如果我們對Url進行MD5或是SHA1進行加密的方式進行哈希的話,這樣會有一個效率的隱患。不過的確這個問題并不那么復雜。對效率的影響也很小。不過,還好Java自身就已經對String型的數據有哈希的函數可以直接調用:hashCode()


代碼及說明:

LinkSpider.java

public class LinkSpider {private SpiderQueue queue = null;/*** 遍歷從某一節點開始的所有網絡鏈接* LinkSpider* @param startAddress* 開始的鏈接節點*/public void ErgodicNetworkLink(String startAddress) {if (startAddress == null) {return;}SpiderBLL.insertEntry2DB(startAddress);List<WebInfoModel> modelList = new ArrayList<WebInfoModel>();queue = SpiderBLL.getAddressQueue(startAddress, 0);if (queue.isQueueEmpty()) {System.out.println("Your address cannot get more address.");return;}ThreadPoolExecutor threadPool = getThreadPool();int index = 0;boolean breakFlag = false;while (!breakFlag) {// 待訪問隊列為空時的處理if (queue.isQueueEmpty()) {System.out.println("queue is null...");modelList = DBBLL.getUnvisitedInfoModels(queue.MAX_SIZE);if (modelList == null || modelList.size() == 0) {breakFlag = true;} else {for (WebInfoModel webInfoModel : modelList) {queue.offer(webInfoModel);DBBLL.updateUnvisited(webInfoModel);}}}WebInfoModel model = queue.poll();if (model == null) {continue;}// 判斷此網站是否已經訪問過if (DBBLL.isWebInfoModelExist(model)) {// 如果已經被訪問,進入下一次循環System.out.println("已存在此網站(" + model.getName() + ")");continue;}poolQueueFull(threadPool);System.out.println("LEVEL: [" + model.getLevel() + "] NAME: " + model.getName());SpiderRunner runner = new SpiderRunner(model.getAddress(), model.getLevel(), index++);threadPool.execute(runner);SystemBLL.cleanSystem(index);// 對已訪問的address進行入庫DBBLL.insert(model);}threadPool.shutdown();}/*** 創建一個線程池的對象* LinkSpider* @return*/private ThreadPoolExecutor getThreadPool() {final int MAXIMUM_POOL_SIZE = 520;final int CORE_POOL_SIZE = 500;return new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy());}/*** 線程池中的線程隊列已經滿了* LinkSpider* @param threadPool* 線程池對象*/private void poolQueueFull(ThreadPoolExecutor threadPool) {while (getQueueSize(threadPool.getQueue()) >= threadPool.getMaximumPoolSize()) {System.out.println("線程池隊列已滿,等3秒再添加任務");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 獲得線程池中的活動線程數* LinkSpider* @param queue* 線程池中承載線程的隊列* @return*/private synchronized int getQueueSize(Queue queue) {return queue.size();}/*** 接收一個鏈接地址,并調用Python獲取該鏈接下的關聯的所有鏈接list* 將list入庫*/class SpiderRunner implements Runnable {private String address;private SpiderQueue auxiliaryQueue; // 記錄訪問某一個網頁中解析出的網址private int index;private int parentLevel;public SpiderRunner(String address, int parentLevel, int index) {this.index = index;this.address = address;this.parentLevel = parentLevel;}public void run() {auxiliaryQueue = SpiderBLL.getAddressQueue(address, parentLevel);System.out.println("[" + index + "]: " + address);DBBLL.insert2Unvisited(auxiliaryQueue, index);auxiliaryQueue = null;}} }
? 在上面的ErgodicNetworkLink方法代碼中,大家可以看到我們已經把使用Queue保存數據的方式改為使用數據庫存儲。這樣做的好處就是我們不用再為OOM而煩惱了。而且,上面的代碼也使用了線程池。使用多線程來執行在調用Python獲得鏈接列表的操作。

? 而對于哈希Url的做法,可以參考如下關鍵代碼:

/*** 添加單個model到等待訪問的數據庫中* DBBLL* @param model*/public static void insert2Unvisited(WebInfoModel model) {if (model == null) {return;}String sql = "INSERT INTO unvisited_site(name, address, hash_address, date, visited, level) VALUES('" + model.getName() + "', '" + model.getAddress() + "', " + model.getAddress().hashCode() + ", " + System.currentTimeMillis() + ", 0, " + model.getLevel() + ");";DBServer db = null;try {db = new DBServer();db.insert(sql);db.close();} catch (Exception e) {System.out.println("your sql is: " + sql);e.printStackTrace();} finally {db.close();}}


??PythonUtils.java

? 這個類是與Python進行交互操作的類。代碼如下:

public class PythonUtils {// Python文件的所在路徑private static final String PY_PATH = "/root/python/WebLinkSpider/html_parser.py";/*** 獲得傳遞給Python的執行參數* PythonUtils* @param address* 網絡鏈接* @return*/private static String[] getShellArgs(String address) {String[] shellParas = new String[3];shellParas[0] = "python";shellParas[1] = PY_PATH;shellParas[2] = address.replace("\"", "\\\"");return shellParas;}private static WebInfoModel parserWebInfoModel(String info, int parentLevel) {if (BEEStringTools.isEmptyString(info)) {return null;}String[] infos = info.split("\\$#\\$");if (infos.length != 2) {return null;}if (BEEStringTools.isEmptyString(infos[0].trim())) {return null;}if (BEEStringTools.isEmptyString(infos[1].trim()) || infos[1].trim().equals("http://") || infos[1].trim().equals("https://")) {return null;}WebInfoModel model = new WebInfoModel();model.setName(infos[0].trim());model.setAddress(infos[1]);model.setLevel(parentLevel + 1);return model;}/*** 調用Python獲得某一鏈接下的所有合法鏈接* PythonUtils* @param shellParas* 傳遞給Python的執行參數* @return*/private static SpiderQueue getAddressQueueByPython(String[] shellParas, int parentLevel) {if (shellParas == null) {return null;}Runtime r = Runtime.getRuntime();SpiderQueue queue = null;try {Process p = r.exec(shellParas);BufferedReader bfr = new BufferedReader(new InputStreamReader(p.getInputStream()));queue = new SpiderQueue();String line = "";WebInfoModel model = null;while((line = bfr.readLine()) != null) { // System.out.println("----------> from python: " + line);if (BEEStringTools.isEmptyString(line.trim())) {continue;}if (HttpBLL.isErrorStateCode(line)) {break;}model = parserWebInfoModel(line, parentLevel);if (model == null) {continue;}queue.offer(model);}model = null;line = null;} catch (IOException e) {e.printStackTrace();} finally {r = null;}return queue;}/*** 調用Python獲得某一鏈接下的所有合法鏈接* PythonUtils* @param address* 網絡鏈接* @return*/public static SpiderQueue getAddressQueueByPython(String address, int parentLevel) {return getAddressQueueByPython(getShellArgs(address), parentLevel);} }

遇到的問題:

1.請使用Python2.7

? 因為Python2.6中HTMLParser還是有一些缺陷的,例如下圖中展示的。不過在Python2.7中,這個問題就不再是問題了。

??


2.數據庫崩潰了

? 數據庫崩潰的原因可能是待訪問的數據表中的數據過大引起的。

??


3.對數據庫的同步操作

? 上面的做法是對數據庫操作進行同步時出現的問題,如果不進行同步,我們會得到數據庫連接數超過最大連接數的異常信息。對于這個問題有望在下篇文章中進行解決。

? 不知道大家對上面的做法有沒有什么疑問。當然,我希望你有一個疑問就是在于,我們去同步數據庫的操作。當我們開始進行同步的時候就已經說明我們此時的同步只是做了單線程的無用功。因為我開始以為對數據庫的操作是需要同步的,數據庫是一個共享資源,需要互斥訪問(如果你學習過“操作系統”,對這些概念應該不會陌生)。實際上還是單線程,解決的方法就是不要對數據庫的操作進行同步操作。而這些引發的數據庫連接數過大的問題,會在下篇文章中進行解決。

總結

以上是生活随笔為你收集整理的网络爬虫:使用多线程爬取网页链接的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 69精品一区二区 | www.婷婷| 久久午夜精品视频 | 日本大胆裸体做爰视频 | 久久久久久久久久综合 | 天堂成人国产精品一区 | 五月激情视频 | 污污在线免费观看 | 成人在线视频免费 | 最近最新mv字幕观看 | 久久久精品视频在线观看 | 重口h文| 欧美综合亚洲 | 四川丰满妇女毛片四川话 | 成人高潮片免费视频 | 日韩精品一区二区三区免费视频 | 天天做天天爱天天操 | 国产精品99无码一区二区视频 | 六月综合网 | 午夜精品久久久久久久 | 中文字幕日日夜夜 | 成人69视频 | 国产在线播放一区二区 | 亚洲av综合色区无码一区 | 天天摸日日摸 | 人人草人人看 | 亚洲午夜久久久久久久国产 | 深夜国产在线 | 九九热九九热 | 三级网站在线播放 | 成人深夜在线 | 久久中文字幕人妻 | 麻豆传媒在线观看 | 国产视频日韩 | avtt香蕉久久 | 亚洲av无一区二区三区久久 | 999在线视频| 午夜一区二区三区免费 | 国产欧美日韩精品在线观看 | 福利电影一区二区三区 | 一色屋免费视频 | 欧美私人情侣网站 | 一区二区三区在线不卡 | 98国产视频 | 污污的视频软件 | 九九九九精品九九九九 | 中文字幕丰满乱子伦无码专区 | 国产午夜在线观看 | 熟女人妻aⅴ一区二区三区60路 | 97视频播放 | 韩国禁欲系高级感电影 | 亚洲人人爽 | 精品国产免费观看 | 久久国产麻豆 | 麻豆影视免费观看 | 久久综合桃花网 | jizz色| 成人高潮片免费 | 日本少妇高潮喷水xxxxxxx | 久久爱一区二区 | 搡老熟女老女人一区二区 | 久草午夜 | 超碰不卡| 国产中文字幕网 | 一区二区三区激情 | 亚洲性图第一页 | 成年人视频免费 | 日日爱886| 国产肉体ⅹxxx137大胆 | 在线成人观看 | 三区在线视频 | 国产va视频 | 色女生影院 | 国产剧情一区 | aaa亚洲精品| 调教少妇视频 | 日韩精品一区二区三区中文字幕 | 亚洲欧洲日韩在线 | 在线观看成年人视频 | 深夜福利91 | 青青草免费在线 | 99热r| 8x8ⅹ成人永久免费视频 | xxx久久久 | 久久天天躁狠狠躁夜夜躁2014 | 天堂福利视频 | 欧美性做爰免费观看 | 国产伦精品一区三区精东 | 亚洲国产高清视频 | 久久亚洲综合色 | 国产日韩欧美另类 | 美丽的姑娘在线观看免费 | 成人18视频免费69 | 国产一区二区视频播放 | 成人不卡在线 | 欧美精品做受xxx性少妇 | 人妻妺妺窝人体色www聚色窝 | 亚洲乱亚洲乱妇 | 色五婷婷 |