چرا لینوکس را دوست دارم: استخراج کلمات کلیدی از سایت نارنجی

اینم یه تجربه پراکنده دیگه!

بسیار پیش میاد که بازار بعضی چیزها توی مملکت‌ما بصورت ناگهانی داغ بشه و باز سرد بشه. این جو ایجاد شده بعضی وقت‌ها خوب و بعضی وقت‌ها بده و بعضی وقت‌ها دامن من رو هم میگیره. حدودا یک سال پیش تب درست کردن سایتی که خبر جمع کنه و اخبار رو بصورت اتوماتیک دسته بندی کنه داغ بود. بصورت قارچی سرویس‌هایی که اخبار رو جمع آوری میکردن سر از خاک در آوردن و موفق شدن یا شکست خوردن. در این حین من به این نتیجه رسیدم که خوبه که من هم به عنوان یه موضوع جانبی و سرگرمی این کار رو انجام بدم. یکی از مشکلاتی که توی این کار داشتم این بود که دسته بندی درست مطالب تاثیر بسیار زیادی توی احتمال رسیدن افراد به این سایت داره. اما مهمترین چیزی که نبود این بود که لیست کلمات کلیدی فارسی اونم تو حوزه تکنولوژی نبود. پس من تصمیم گرفتم به کمک وب این لیست رو بوجود بیارم

اون موقع تازه نارنجی رو تعطیل کرده بودند اما هنوز سایت سرجاش بود. به علت کیفیت خوب کار دوستان توی نارنجی هم تگ‌هایی که به مطالب داده بودن به عنوان لیست کلمات کلیدی قابل اعتماد بود. پس من به این نتیجه رسیدم که یه سری اسکریپت بنویسم که این کلمات رو استخراج کنم. یکم توی اینترنت گشتم و ابزارهای مربوط به استخراج اطلاعات از صفحات وب(web scraping) رو بررسی کردم. آخرش به این نتیجه رسیدم که پایتون و beautiful soup رو پیدا کردم و به کمک اون اسکریپ رو نوشتم.

خب اول از همه تابع استخراج تمامی url های تمام پست‌های نارنجی هست

 

def getNarenjiURLs(inUrl):
	lst = []
	url = 'http://narenji.ir/'

	# Check if it is a valid URL
	if 'http://' in inUrl:
		url = inUrl
	else:
		url = 'http://' + inUrl

	# logging start time
	rst = time.time()
	# getting URL content
	r  = requests.get(url)
	#logging end time
	rend = time.time()
	
	pst = time.time()
	data = r.text
	 
	# parsing content using Beautiful soupe
	soup = BeautifulSoup(data, 'lxml')

	for link in soup.find_all('div',class_='views-field views-field-title'):
		for a in link.find_all('a'):
			if a['href'].startswith('/'):
				lst.append('http://narenji.ir' + a['href'])
			else :
				lst.append(a['href'])
	pend = time.time()
	#print (' r Time = ' + str(rend - rst) +' p Time ' + str(pend - pst))
	return lst

خب بعدش نوبت میرسه به اینکه توی هر صفحه کلمات کلیدی رو استخراج کنم. با بررسی سورس html سایت متوجه شدم که تمام کلمات کلیدی یه الگوی خاص دارن و اون الگو قابل استخراج بود. کد این کار هم در پایین اومده

def getNarenjiKeywords(inUrl):
	lst = []
	url = ""
	if 'http://' in inUrl:
		url = inUrl
	else:
		url = 'http://' + inUrl

	# logging start time	 
	rst = time.time()
	# getting URL content
	r  = requests.get(url)
	#logging end time
	rend = time.time()

	pst = time.time()

	# parsing content using Beautiful
	data = r.text
	 
	soup = BeautifulSoup(data, 'lxml')
	 
	for link in soup.find('div',class_='meta').findAll('a'):
		lst.append(link.text)
	pend = time.time()

	#print (' r Time = ' + str(rend - rst) +' p Time = ' + str(pend - pst))

	return lst

با توجه به بررسی‌هایی که انجام دادم متوجه شدم اگه این کار رو بخوام بصورت متوالی انجام بدم کلی طول خواهد کشید پس تلاش کردم که کار رو بصورت موازی انجام بدم. پس به ازای هر درخواست یه Thread بالا آوردم و بصورت موازی این کارها رو انجام دادم. یه کد warpper رو این دوتا تابع بصورت زیر نوشتم.

def doUrlWork():
	global urlList
	while True:
		url=qGetUrl.get()
		tmpUrl = getNarenjiURLs(url)
		print ( str(len(tmpUrl)) + ' URLs found')
		urlList.update(set(tmpUrl))
		qGetUrl.task_done()

def doKeywordWork():
	global keys
	while True:
		url=qGetKeywords.get()
		tmpKey = getNarenjiKeywords(url)
		print ( str(len(tmpKey)) + ' keys found')
		keys.update(set(tmpKey))
		qGetKeywords.task_done()

برای اینکه تعداد Thread ها زیاد نشه از یه صف استفاده کردم که الان یادم نیست. همچنین از مجموعه‌های پایتون برای حذف کلمات تکراری استفاده کردم.

پس اگه بخوام کل اسکریپت بصورت یکجا بیارم میشه:

import timeit
import time
from bs4 import BeautifulSoup
import urllib2
import requests
from Queue import Queue
from threading import Thread
import codecs

keys = set([])
urlList = set([])

concurrent = 10

def doUrlWork():
	global urlList
	while True:
		url=qGetUrl.get()
		tmpUrl = getNarenjiURLs(url)
		print ( str(len(tmpUrl)) + ' URLs found')
		urlList.update(set(tmpUrl))
		qGetUrl.task_done()

def doKeywordWork():
	global keys
	while True:
		url=qGetKeywords.get()
		tmpKey = getNarenjiKeywords(url)
		print ( str(len(tmpKey)) + ' keys found')
		keys.update(set(tmpKey))
		qGetKeywords.task_done()

def getNarenjiKeywords(inUrl):
	lst = []
	url = ""
	if 'http://' in inUrl:
		url = inUrl
	else:
		url = 'http://' + inUrl

	# logging start time	 
	rst = time.time()
	# getting URL content
	r  = requests.get(url)
	#logging end time
	rend = time.time()

	pst = time.time()

	# parsing content using Beautiful
	data = r.text
	 
	soup = BeautifulSoup(data, 'lxml')
	 
	for link in soup.find('div',class_='meta').findAll('a'):
		lst.append(link.text)
	pend = time.time()

	#print (' r Time = ' + str(rend - rst) +' p Time = ' + str(pend - pst))

 

به این ترتیب کلمات کلیدی نارنجی بصورت موازی استخراج شد و ذخیره شد!

همین!

لذت برنامه نویسی! در C متغیرها کجا تعریف می‌شوند

اینم یه تجربه پراکنده دیگه!

من مدت‌هاست که به عنوان یکی از کارهای اصلی به زبان C برای سیستم‌های Embeded برنامه می‌نویسم. یعنی یه دستگاه محدود رو آماده میکنم که یه کار خاص رو انجام بده.

یکی از بزرگترین ویژگی‌های این دستگاه‌ها اینه که حافظه محدودی دارن و بعضی وقت‌ها بایستی حواس برنامه نویس به اینکه اولا چقدر حافظه مصرف شده و دوما اینکه متغیرها‌ی برنامه ای که مینویسه کجای حافظه تعریف میشن باشه.

اگه با اسمبلی آشنا باشید هر برنامه از سه قسمت Data, Code, Stack تعریف میشه. که خود  Data Segment هم از سه بخش Heap, Data, BSS تشکیل میشه. که توضیح هرکدوم این بخش‌ها داستان متفاوتی داره. حالا توی شبه کد پایین توضیح میدم که  اینها با هم چه تفاوتی داره

int a;

void myFunc(void)
{
	int b;
	static int c;
	int *d = malloc(sizeof(int));

	...

	return;
}

توی این شبه کد متغیر global به نام a وجود داره که توی data و یا ‌BSS تعریف میشه. متغیر b چون یک متغیر محلیه توی stack تعریف میشه. متغیر c یک متغیر محلی استایتک هست و به دلیل استاتیک بودنش توی data و یا ‌BSS تعریف میشه.  پوینتر d همه به یه آدرس توی heap اشاره میکنه چون این متغیر بصورت داینامیک تعریف شده و متغیر داینامیک در heap درست میشه. تفاوت data و BSS در اینه که متغیرهایی که مقدار اولیه غیر صفر دارن توی data تعریف میشن و متغیرهایی که مقدار اولیه ندارن و یا صفرن توی BSS تعریف میشن.

حالا مشکلی که وجود داشت اینه که من متغیرهای بزرگی توی متن تابع هام داشتم که باعث میشد حجم محدود stack موجود پر بشه و نرم افزار به خطای زمان اجرا بر بخوره. که با تغییر اونها به متغیرهای static محل نگهداری اونها به BSS یا Data تغییر مکان پیدا کرد و مساله حل شد.

همین!