<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-3533262157176083768</atom:id><lastBuildDate>Mon, 09 Jan 2012 11:21:51 +0000</lastBuildDate><category>linux</category><category>Яндекс</category><category>tools</category><category>парсинг Яндекса</category><category>теория</category><category>опупеть</category><category>CURL</category><category>Curlpas</category><category>TTreeView</category><category>разработка</category><category>парсинг</category><category>парсинг выдачи поисковиков</category><category>TWebBrowser</category><category>Synapse</category><category>БД</category><category>XML</category><category>Indy</category><category>узкие места</category><category>парсер</category><category>на примерах читателей</category><category>отладка</category><category>форумы</category><category>PHP</category><category>coHTMLDocument</category><category>web-автоматизация</category><category>RSS</category><category>UrlDownloadToFile</category><category>IHTMLDocument2</category><category>RegExp</category><category>способы применения парсинга</category><category>компоненты и библиотеки Delphi</category><category>базы</category><category>XMLDocument</category><category>Delphi</category><category>объектная модель</category><category>алгоритмы</category><category>блог</category><category>RSSAdder</category><category>код</category><category>csv</category><category>регулярные выражения</category><category>парсинг Google</category><category>IdHTTP</category><category>Simple HTML DOM</category><category>жизнь</category><title>Парсинг от А до Я</title><description>Блог о программировании парсеров и web-автоматизации</description><link>http://parsing-and-i.blogspot.com/</link><managingEditor>noreply@blogger.com (Masha)</managingEditor><generator>Blogger</generator><openSearch:totalResults>103</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/parsing-lessons" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="parsing-lessons" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">parsing-lessons</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-6524557064873721043</guid><pubDate>Thu, 10 Mar 2011 10:02:00 +0000</pubDate><atom:updated>2011-03-10T13:47:49.283+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">TWebBrowser</category><title>Delphi: Как удалить cookies из TWebBrowser?</title><description>В этой статье не будет ничего скандально нового :) Просто уже 2 комментатора спросили в заметке о &lt;a href="http://parsing-and-i.blogspot.com/2009/06/cookies-twebbrowser.html"&gt;cookies в TWebBrowser&lt;/a&gt;, как их удалять. В гугле, ясно дело, решение найти просто, но почему-то для кого-то проще спросить и ждать :)&lt;br /&gt;&lt;br /&gt;В общем, если на мой блог о парсинге приходят по этому запросу, то надо на него ответить. :)&lt;br /&gt;&lt;br /&gt;Это готовое решение, взятое из инета и опробованное мной. В результате будут подчищены все файлы с куками, хранящиеся в &lt;span style="font-style:italic;"&gt;c:\Documents and Settings\Имя_пользователя\Cookies&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;uses&lt;br /&gt;  WinInet;&lt;br /&gt;&lt;br /&gt;procedure DeleteIECache;&lt;br /&gt;var&lt;br /&gt;  lpEntryInfo: PInternetCacheEntryInfo;&lt;br /&gt;  hCacheDir: LongWord;&lt;br /&gt;  dwEntrySize: LongWord;&lt;br /&gt;begin&lt;br /&gt;  dwEntrySize := 0;&lt;br /&gt;  FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), dwEntrySize);&lt;br /&gt;  GetMem(lpEntryInfo, dwEntrySize);&lt;br /&gt;  if dwEntrySize &amp;gt; 0 then lpEntryInfo^.dwStructSize := dwEntrySize;&lt;br /&gt;  hCacheDir := FindFirstUrlCacheEntry(nil, lpEntryInfo^, dwEntrySize);&lt;br /&gt;  if hCacheDir &amp;lt;&amp;gt; 0 then &lt;br /&gt;  begin&lt;br /&gt;    repeat&lt;br /&gt;      DeleteUrlCacheEntry(lpEntryInfo^.lpszSourceUrlName);&lt;br /&gt;      FreeMem(lpEntryInfo, dwEntrySize);&lt;br /&gt;      dwEntrySize := 0;&lt;br /&gt;      FindNextUrlCacheEntry(hCacheDir, TInternetCacheEntryInfo(nil^), dwEntrySize);&lt;br /&gt;      GetMem(lpEntryInfo, dwEntrySize);&lt;br /&gt;      if dwEntrySize &amp;gt; 0 then lpEntryInfo^.dwStructSize := dwEntrySize;&lt;br /&gt;    until not FindNextUrlCacheEntry(hCacheDir, lpEntryInfo^, dwEntrySize);&lt;br /&gt;  end;&lt;br /&gt;  FreeMem(lpEntryInfo, dwEntrySize);&lt;br /&gt;  FindCloseUrlCache(hCacheDir);&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Для чего может пригодиться очистка кукисов?&lt;/span&gt; Ну, например, вы написали какое-то свое приложение с TWebBrowser и заходите на определенный сайт через список прокси... ;)&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Сегодня объявляю гранд мерси &lt;span style="font-weight:bold;"&gt;akkadites&lt;/span&gt;, автору блога &lt;a href="http://u-proga.co.cc/"&gt;"Обзор полезного софта"&lt;/a&gt;, и &lt;a href="http://seorit.ru/"&gt;Seorit.ru&lt;/a&gt; за обзоры &lt;a href="http://parsing-and-i.blogspot.com/2009/08/rssadder-rss-rss.html"&gt;RSSAdder&lt;/a&gt;-а.&lt;br /&gt;___&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-6524557064873721043?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DXpN43lY5G_pty_G2uq_27y7H0M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DXpN43lY5G_pty_G2uq_27y7H0M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/DXpN43lY5G_pty_G2uq_27y7H0M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DXpN43lY5G_pty_G2uq_27y7H0M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/q0GjnWA63kI" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2011/03/delphi-how-to-delete-cookies-of.html</link><author>noreply@blogger.com (Masha)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-8519586850170829226</guid><pubDate>Fri, 25 Feb 2011 10:35:00 +0000</pubDate><atom:updated>2011-02-25T13:50:53.707+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><title>Delphi XE Starter за 199$ (5900 рублей)</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-epUoQTFFqQ0/TWeJNZrpDoI/AAAAAAAAAUc/RzJ5mFZ-aCY/s1600/delphi-xe.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 105px; height: 99px;" src="http://4.bp.blogspot.com/-epUoQTFFqQ0/TWeJNZrpDoI/AAAAAAAAAUc/RzJ5mFZ-aCY/s400/delphi-xe.gif" border="0" alt="Delphi XE" id="BLOGGER_PHOTO_ID_5577577526846885506" /&gt;&lt;/a&gt;Только ленивый программист-блоггер не написал еще об этой акции, проводимой Embarcadero. Продукт &lt;a href="http://www.embarcadero.com/products/delphi/starter"&gt;Delphi XE Starter Edition&lt;/a&gt; можно купить за 199 баксов (или за 5900 рублей — &lt;a href="http://ibase.ru/prices/embarcadero.htm"&gt;ссылка на прайс&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Естественно, что у такой лицензии есть ограничения (и для кого-то они критические). Например, она может быть использована только в том случае, если годовой профит от разработанного программного обеспечения не превышает $ 1000. То есть практически никакого профита ;) Но почему-то мне эта акция приглянулась и я хочу ей воспользоваться. Тем более, что при покупке Delphi XE покупатель автоматически получает Delphi 2010, 2009, 2007 и 7.&lt;br /&gt;&lt;br /&gt;Буду использовать эту лицензию конкретно дома (на работе все есть).&lt;br /&gt;&lt;br /&gt;А вы как думаете, стоит оно того?&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;P.S. Всем дельным комментаторам большое спасибо за комментарии. И вообще большое спасибо всем читателям блога за то, что вы есть. Как только работы поубавится — все разберу.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-8519586850170829226?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/oGjADWZY-1TPECZq83EihZLojUw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/oGjADWZY-1TPECZq83EihZLojUw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/oGjADWZY-1TPECZq83EihZLojUw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/oGjADWZY-1TPECZq83EihZLojUw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/_i6hdtoEyIw" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2011/02/delphi-xe-starter-199-5900.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-epUoQTFFqQ0/TWeJNZrpDoI/AAAAAAAAAUc/RzJ5mFZ-aCY/s72-c/delphi-xe.gif" height="72" width="72" /><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-3159810757004201156</guid><pubDate>Thu, 16 Dec 2010 11:56:00 +0000</pubDate><atom:updated>2010-12-16T15:18:46.002+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Delphi: работа с классами, унаследованными от TList</title><description>Еще немного расскажу о своих "привычках" в программировании. Очень часто приходится работать со списком однотипных объектов. В этом случае создаю класс для объекта и класс для списка объектов, унаследованный от TList.&lt;br /&gt;&lt;br /&gt;Применяю это и в персерах. Вернее, при написании приложений, где необходим парсинг и последующая обработка полученных данных, но где использование базы данных было бы излишеством.&lt;br /&gt;&lt;br /&gt;Самый простой пример такой объектной организации:&lt;br /&gt;&lt;pre&gt;TBASetting  = class&lt;br /&gt;  BA              : string;&lt;br /&gt;  MM_CONST        : integer;&lt;br /&gt;  STRIKESTEP,&lt;br /&gt;  MIN_STRIKE,&lt;br /&gt;  MAX_STRIKE      : double;  &lt;br /&gt;public&lt;br /&gt;  constructor Create(const aPath: string; const aParams: TStrings); overload;&lt;br /&gt;  ...&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;TBASettings = class(TList)&lt;br /&gt;  ...&lt;br /&gt;  function LoadFromINI (IniFile : TIniFile) : boolean;&lt;br /&gt;  function SaveToINI (IniFile: TIniFile) : boolean;&lt;br /&gt;  procedure Clear; override;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;Остановлюсь подробнее на следующих пунктах:&lt;br /&gt;- создание объектов, заполнение списка;&lt;br /&gt;- освобождение памяти при "очистке" списка.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;Создание объектов и добавление их в список&lt;/h2&gt;&lt;br /&gt;Опять же, два варианта.&lt;br /&gt;1. &lt;span style="font-weight:bold;"&gt;Создание одиночного объекта (выделение памяти, присвоение значений полям), добавление в список.&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;tmpSet := TBASetting.Create;&lt;br /&gt;tmpSet.BA := sBA;&lt;br /&gt;tmpSet.MM_CONST := iMM_CONST;&lt;br /&gt;tmpSet.STRIKESTEP := dSTRIKESTEP;&lt;br /&gt;tmpSet.MIN_STRIKE := dMIN_STRIKE;&lt;br /&gt;tmpSet.MAX_STRIKE := dMAX_STRIKE;&lt;br /&gt;BASettings.Add(tmpSet);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. &lt;span style="font-weight:bold;"&gt;Массовое заполнение списка объектами&lt;/span&gt; (групповое добавление объектов). Этот вариант удобен, если, например, считывать данные из ini-файла или XML.&lt;br /&gt;&lt;pre&gt;function TBASettings.LoadFromINI(IniFile: TIniFile): boolean;&lt;br /&gt;var&lt;br /&gt;  Sections,&lt;br /&gt;  SecParams   : TStringList;&lt;br /&gt;  i           : integer;&lt;br /&gt;  sVal        : string;&lt;br /&gt;begin&lt;br /&gt;  Result := false;&lt;br /&gt;  Sections := TStringList.Create;&lt;br /&gt;  try&lt;br /&gt;    IniFile.ReadSections(Sections);&lt;br /&gt;    SecParams := TStringList.Create;&lt;br /&gt;    try&lt;br /&gt;      for i:=0 to Sections.Count-1 do&lt;br /&gt;      begin&lt;br /&gt;        sVal := Sections[i];&lt;br /&gt;        // ну, тут всякие условия, проверки, код для "отсева" и т.д.&lt;br /&gt;        if sVal &lt;&gt; 'COMMON' then&lt;br /&gt;          begin&lt;br /&gt;            IniFile.ReadSectionValues(sVal, SecParams);&lt;br /&gt;            if SecParams.Count &gt; 0 then&lt;br /&gt;              begin&lt;br /&gt;                BASettings.Add(TBASetting.Create(sVal, SecParams));&lt;br /&gt;              end&lt;br /&gt;          end;&lt;br /&gt;      end;&lt;br /&gt;    finally&lt;br /&gt;      SecParams.Free;&lt;br /&gt;    end;&lt;br /&gt;  finally&lt;br /&gt;    Sections.Free;&lt;br /&gt;  end;&lt;br /&gt;  Result := true;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;constructor TBASetting.Create(const aPath: string;&lt;br /&gt;  const aParams: TStrings);&lt;br /&gt;begin&lt;br /&gt;  BA            := aPath;&lt;br /&gt;  MM_CONST      := StrToIntDef(aParams.Values['MM_CONST'], 4);&lt;br /&gt;  STRIKESTEP    := StrToFloatDef(aParams.Values['STRIKESTEP'], 0);&lt;br /&gt;  MIN_STRIKE    := StrToFloatDef(aParams.Values['MIN_STRIKE'], 0);&lt;br /&gt;  MAX_STRIKE    := StrToFloatDef(aParams.Values['MAX_STRIKE'], 0);&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Очистка списка объектов (наследника TList)&lt;/h2&gt;&lt;br /&gt;При удалении единичных экземпляров (объектов) из списка надо освобождать память каждого объекта, а уже потом удалять его из списка.&lt;br /&gt;&lt;br /&gt;Что делать при удалении всего списка? У TList есть метод Free, который освобождает память и удаляет объект. Казалось бы, надо у нашего списка перегрузить этот метод? Но правильнее перегрузить метод Clear (который, в свою очередь, вызывается из Free, а Free вызывается FreeAndNil-ом).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;TBASettings = class(TList)&lt;br /&gt;  ...&lt;br /&gt;  function LoadFromINI (IniFile : TIniFile) : boolean;&lt;br /&gt;  function SaveToINI (IniFile: TIniFile) : boolean;&lt;br /&gt;  procedure Clear; override;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TBASettings.Clear;&lt;br /&gt;var&lt;br /&gt;  i : integer;&lt;br /&gt;  tmpSet  : TBASetting;&lt;br /&gt;begin&lt;br /&gt;  for i := 0 to Count-1 do&lt;br /&gt;    begin&lt;br /&gt;      tmpSet := TBASetting(Items[i]);&lt;br /&gt;      tmpSet.Free;&lt;br /&gt;    end;&lt;br /&gt;  inherited;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если в составе класса в качестве полей есть экземпляры других классов, то надо:&lt;br /&gt;- перегрузить конструктор, выделить в нем память под эти поля;&lt;br /&gt;- перегрузить деструктор объекта, в котором освободить память, выделенную при создании.&lt;br /&gt;&lt;br /&gt;Листинг для примера:&lt;br /&gt;&lt;pre&gt;TBASetting  = class&lt;br /&gt;  BA              : string;&lt;br /&gt;  PUTNAME,&lt;br /&gt;  CALLNAME        : TStringList;&lt;br /&gt;  MM_CONST        : integer;&lt;br /&gt;  STRIKESTEP,&lt;br /&gt;  MIN_STRIKE,&lt;br /&gt;  MAX_STRIKE      : double;&lt;br /&gt;public&lt;br /&gt;  ...&lt;br /&gt;  constructor Create(const aPath: string; const aParams: TStrings); overload;&lt;br /&gt;  constructor Create; overload;&lt;br /&gt;  destructor Destroy; override;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;constructor TBASetting.Create;&lt;br /&gt;begin&lt;br /&gt;  PUTNAME := TStringList.Create;&lt;br /&gt;  CALLNAME := TStringList.Create;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;destructor TBASetting.Destroy;&lt;br /&gt;begin&lt;br /&gt;  PUTNAME.Free;&lt;br /&gt;  CALLNAME.Free;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;div class="info"&gt;Я тут привожу условный листинг, не особо заботясь о "классических правилах" работы с классами. Вообще, в небольших проектиках, которые пишу под свои задачи, не парюсь над защищенностью наследуемых и ненаследуемых методов, всяких протектедах и доступах к полям. Считаю, что это лишнее, хотя, может, кто-то с этим и не согласится.&lt;/div&gt;&lt;br /&gt;На сегодня это все, о чем я хотела поведать. Пойду дальше программировать :)&lt;br /&gt;&lt;br /&gt;P.S.: Производительность труда заметно повышается, когда дома ломается компьютер. На работе приходится брать себя в руки и работать! :D&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-3159810757004201156?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qHK52GIf22QAAE8pizP6oD_UBQc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qHK52GIf22QAAE8pizP6oD_UBQc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/qHK52GIf22QAAE8pizP6oD_UBQc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qHK52GIf22QAAE8pizP6oD_UBQc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/y1TnygGVHEM" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/12/delphi-tlist.html</link><author>noreply@blogger.com (Masha)</author><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-7710400773677741512</guid><pubDate>Mon, 15 Nov 2010 19:38:00 +0000</pubDate><atom:updated>2010-11-16T00:27:37.442+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">Curlpas</category><category domain="http://www.blogger.com/atom/ns#">CURL</category><title>Delphi: работа с cURL с помощью библиотеки-обертки Curlpas</title><description>&lt;a href="http://parsing-and-i.blogspot.com/2009/03/internet-direct-indy.html" title="Знакомство с Indy"&gt;Indy&lt;/a&gt; и &lt;a href="http://parsing-and-i.blogspot.com/2010/02/synapse-in-delphi-first-steps.html" title="Знакомство с Synapse"&gt;Synapse&lt;/a&gt; вам надоели? Самое время попробовать что-нибудь новенькое :)&lt;br /&gt;&lt;br /&gt;Если кто-нибудь, начитавшись статей про &lt;a href="http://parsing-and-i.blogspot.com/2009/09/curl-first-steps.html" title="Начало работы с cURL в PHP"&gt;cURL&lt;/a&gt; (я писала не раз про работу с cURL в PHP), решит попробовать работать с этой библиотекой и в Delphi, то ничего сложного в этом нет.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Установка CURL и работа с этой библиотекой в Delphi 7&lt;/h2&gt;&lt;br /&gt;1. Скачиваем файлы библиотеки libcurl с сайта разработчика по &lt;a href="http://curl.haxx.se/download.html" rel="nofollow" title="Исходники libcurl"&gt;ссылке&lt;/a&gt;. Из всего разнообразия выбираем те, что подходят для нашей операционки. В моем случае это:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/TOGgNsTd88I/AAAAAAAAASo/__Pn1WL2yYo/s1600/win_lib_download.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 70px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/TOGgNsTd88I/AAAAAAAAASo/__Pn1WL2yYo/s400/win_lib_download.gif" border="0" alt="libcurl для Windows" title="libcurl для Windows" id="BLOGGER_PHOTO_ID_5539885173733979074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Содержимое папки &lt;span style="font-weight:bold;"&gt;bin&lt;/span&gt; из скачанного архива копируем в &lt;span style="font-style:italic;"&gt;C:\WINDOWS\system32\&lt;/span&gt;. Там несколько файлов dll:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/TOGgsrP7lwI/AAAAAAAAASw/1z56j1bYDWQ/s1600/curl_lib_files.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 190px; height: 151px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/TOGgsrP7lwI/AAAAAAAAASw/1z56j1bYDWQ/s400/curl_lib_files.gif" border="0" alt="Файлы библиотеки libcurl" id="BLOGGER_PHOTO_ID_5539885706026653442" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. Скачиваем готовую библиотеку-обертку &lt;a href="http://sourceforge.net/projects/curlpas/files/" rel="nofollow" title="Библиотека для работы с CURL в Delphi — CurlPas"&gt;CurlPas&lt;/a&gt; для работы с libcurl.&lt;br /&gt;&lt;br /&gt;3. Распаковываем архив куда-нибудь, например, в &lt;span style="font-style:italic;"&gt;C:\Program Files\Borland\Delphi7\Source\Curlpas\&lt;/span&gt;. Обратите внимание, в архиве есть еще документация и примеры использования (папка demo).&lt;br /&gt;&lt;br /&gt;4. Запускаем батник &lt;span style="font-style:italic;"&gt;Makewin.bat&lt;/span&gt; с параметром &lt;span style="font-style:italic;"&gt;src&lt;/span&gt; (или &lt;span style="font-style:italic;"&gt;all&lt;/span&gt;, чтобы уж не мелочиться :) ).&lt;br /&gt;&lt;br /&gt;Смотрим лог выполнения инструкций. Там должно быть что-то вроде "все успешно" по каждой директиве.&lt;br /&gt;&lt;br /&gt;5. Можно приступать к тестированию библиотеки. Создаем приложение.&lt;br /&gt;&lt;br /&gt;В uses прописываем:&lt;br /&gt;&lt;pre&gt;uses&lt;br /&gt;  ... curlobj;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Помещаем на форму кнопку, в обработчике нажатия пишем код:&lt;br /&gt;&lt;pre&gt;procedure TForm1.Button1Click(Sender: TObject);&lt;br /&gt;var &lt;br /&gt;  Curl: TCurl;&lt;br /&gt;begin&lt;br /&gt;  Curl := TCurl.Create(nil);&lt;br /&gt;  Curl.URL := 'http://parsing-and-i.blogspot.com/';&lt;br /&gt;  Curl.OutputFile := 'curl.html';&lt;br /&gt;  if not Curl.Perform then&lt;br /&gt;    ShowMessage(Curl.ErrorString);&lt;br /&gt;  Curl.Free;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;Запускаем. Все должно без проблем скомпилиться, в итоге после нажатия кнопки в папке с проектом появится файл curl.html с html-кодом главной страницы &lt;a href="http://parsing-and-i.blogspot.com/"&gt;моего блога&lt;/a&gt; :)&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Возможные ошибки при установке Curlpas&lt;/h2&gt;&lt;br /&gt;По каким-то причинам могут не "подхватиться" библиотеки. Тогда после запуска приложения может появиться что-то типа такого сообщения:&lt;br /&gt;&lt;div class="error"&gt;Приложению не удалось запуститься, поскольку libcurl-3.dll не был найден. Повторная установка приложения может решить эту проблему.&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_dS8cdXcqCug/TOGjHT-ncTI/AAAAAAAAATA/LA3vM0HzRsE/s1600/curl_error.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 61px;" src="http://4.bp.blogspot.com/_dS8cdXcqCug/TOGjHT-ncTI/AAAAAAAAATA/LA3vM0HzRsE/s400/curl_error.jpg" border="0" alt="Ошибка при установке libcurl в Delphi" title="Ошибка при установке libcurl в Delphi" id="BLOGGER_PHOTO_ID_5539888362659737906" /&gt;&lt;/a&gt;&lt;br /&gt;Если компилятор запрашивает файл &lt;span style="font-style:italic;"&gt;libcurl-3.dll&lt;/span&gt;, а в скачанном пакете &lt;span style="font-weight:bold;"&gt;libcurl&lt;/span&gt; у вас файл называется &lt;span style="font-style:italic;"&gt;libcurl.dll&lt;/span&gt; (зависит от версии библиотеки), то найдите файл &lt;span style="font-style:italic;"&gt;curl_h.pas&lt;/span&gt; (из папки &lt;span style="font-style:italic;"&gt;C:\Program Files\Borland\Delphi7\Source\Curlpas\src\&lt;/span&gt;, если вы устанавливали по моей инструкции) и в нем исправьте название файла.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_dS8cdXcqCug/TOGiGxw3hSI/AAAAAAAAAS4/f4dXSnR5hIM/s1600/curl_h.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 361px;" src="http://2.bp.blogspot.com/_dS8cdXcqCug/TOGiGxw3hSI/AAAAAAAAAS4/f4dXSnR5hIM/s400/curl_h.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5539887253963638050" /&gt;&lt;/a&gt;&lt;br /&gt;Потом перекомпилируйте библиотеку.&lt;br /&gt;&lt;br /&gt;Если не находятся какие-то другие dll — еще раз проверьте их наличие в &lt;span style="font-style:italic;"&gt;System32&lt;/span&gt; (или другом каталоге, как у вас принято).&lt;br /&gt;&lt;br /&gt;Вот, в общем-то, и все — установка завершена, можете работать с библиотекой дальше. В папке &lt;span style="font-style:italic;"&gt;doc&lt;/span&gt; — достаточно полная документация. А онлайн документация есть &lt;a href="http://curlpas.sourceforge.net/doc/" rel="nofollow" title="Онлайн-документация по CurlPas"&gt;здесь&lt;/a&gt;.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-7710400773677741512?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/b4Zv7HDykCQKXD8Uo30XVyBX43o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/b4Zv7HDykCQKXD8Uo30XVyBX43o/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/b4Zv7HDykCQKXD8Uo30XVyBX43o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/b4Zv7HDykCQKXD8Uo30XVyBX43o/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/vLQtuy-IMuQ" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/11/delphi-curl-curlpas.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/TOGgNsTd88I/AAAAAAAAASo/__Pn1WL2yYo/s72-c/win_lib_download.gif" height="72" width="72" /><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-4744088686505509160</guid><pubDate>Wed, 10 Nov 2010 12:37:00 +0000</pubDate><atom:updated>2010-11-10T16:48:25.935+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">жизнь</category><title>И снова с вами...</title><description>Два месяца не появлялась, на то были причины. Понемногу начну исправляться: разгребать почту, чистить комменты (очень много спама, а я по старинке привыкла доверять людям и захожу по каждому адресу, указанному комментаторами), смотреть зафоловивших меня в твиттере. Писем в ящике, указанном в профиле, очень много. Это мой неосновной ящик, поэтому только-только добралась. Даже не знаю, как поступить: отвечать или не отвечать на "старые" предложения (наверное, у них уже вышел "срок годности". Тут дилема: предстать невежливой или предстать тормозом :) ).&lt;br /&gt;&lt;br /&gt;Начало осени было очень насыщенным: периоды интенсивной работы чередовались с интенсивным же отдыхом. Съездили с мужем отдохнуть в Таиланд. Несколько фоток.&lt;br /&gt;&lt;br /&gt;Одно из "обычных состояний" — с картой в руках. Я — навигатор :)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_dS8cdXcqCug/TNqS-7HcZAI/AAAAAAAAASQ/0PK-LBP-Gck/s1600/x_d44cddee.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://4.bp.blogspot.com/_dS8cdXcqCug/TNqS-7HcZAI/AAAAAAAAASQ/0PK-LBP-Gck/s320/x_d44cddee.jpg" border="0" alt="Тайланд, Бангкок" id="BLOGGER_PHOTO_ID_5537900301523903490" /&gt;&lt;/a&gt;&lt;br /&gt;Я и кузнечик: кто кого?&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/TNqS_nI6j4I/AAAAAAAAASY/8I6SmI849SY/s1600/x_a278319f.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/TNqS_nI6j4I/AAAAAAAAASY/8I6SmI849SY/s320/x_a278319f.jpg" border="0" alt="Паттайя. Кузнечик"id="BLOGGER_PHOTO_ID_5537900313341235074" /&gt;&lt;/a&gt;&lt;br /&gt;Ну и широко известные тайские мальчики-девочки. Это самые "очевидные". Большинство же такие, что для идентификации надо приглядеться :)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/TNqTAJ946hI/AAAAAAAAASg/JBvj-1trE3c/s1600/x_ae4581cd.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/TNqTAJ946hI/AAAAAAAAASg/JBvj-1trE3c/s320/x_ae4581cd.jpg" border="0" alt="Тайские девочки (в кавычках)"id="BLOGGER_PHOTO_ID_5537900322690230802" /&gt;&lt;/a&gt;&lt;br /&gt;Фотографий привезли — море. Но здесь им, конечно, не место :)&lt;br /&gt;&lt;br /&gt;Замечательная страна. Всерьез подумываем поселиться там и пожить некоторое время. Естественно, что для этого надо трудиться и налаживать источники доходов, не привязанные к месту жительства. В принципе, чем мы сейчас и занимаемся.&lt;br /&gt;&lt;br /&gt;Парсеры на заказ в настоящее время не пишу, обращаться не стоит. Пишу приложения для торговли на бирже с использованием &lt;a href="http://ttools.ru/?page_id=430#10"&gt;QuikOrdersDOM SDK&lt;/a&gt; (SDK для работы с Квиком). Занимаюсь своими проектами (это как раз относится к "налаживанию источников дохода", о котором упоминалось в предыдущем абзаце). Для своих проектов программирую по мелочам, так что, в принципе, писать на блоге есть о чем — в ближайшем будущем посты ожидаются :)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Товарищи!&lt;/span&gt; По поводу &lt;a href="http://parsing-and-i.blogspot.com/2009/08/rssadder-rss-rss.html"&gt;RSSAdder-а&lt;/a&gt;. В свое время я обещала постовые всем, кто напишет обзор у себя на блогах. Я про это не забыла, только вот вычислить написавших мне трудно: blogger не показывает, а самой мне искать совсем некогда. Оставляйте в комментах ссылки на обзоры — все обещанные постовые будут размещены в новых записях на этом блоге.&lt;br /&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Зачем тянуть с объявлением благодарностей? :) Спасибо &lt;span style="font-weight:bold;"&gt;artcher-у&lt;/span&gt;, автору блога &lt;a href="http://artcher.net"&gt;"Web как философия жизни"&lt;/a&gt; за прекрасный &lt;a href="http://artcher.net/2010/10/31/"&gt;обзор RSSAdder-а с картинками&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;Так же спасибо &lt;a href="http://www.seozip.ru/"&gt;SeoZIP-у&lt;/a&gt; ) Желаю удачи в развитии блога!&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-4744088686505509160?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Vtw236-AiI48VlwLoZKdrkRXLxA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Vtw236-AiI48VlwLoZKdrkRXLxA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Vtw236-AiI48VlwLoZKdrkRXLxA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Vtw236-AiI48VlwLoZKdrkRXLxA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/nf2s9TbJp0I" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/11/blog-post.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_dS8cdXcqCug/TNqS-7HcZAI/AAAAAAAAASQ/0PK-LBP-Gck/s72-c/x_d44cddee.jpg" height="72" width="72" /><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-6097858155597548034</guid><pubDate>Tue, 24 Aug 2010 10:41:00 +0000</pubDate><atom:updated>2010-08-24T14:49:54.228+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">RSSAdder</category><title>RSSAdder: обновление конфига</title><description>Приветствую всех, кто пользуется утилитой &lt;a href="http://parsing-and-i.blogspot.com/2009/08/rssadder-rss-rss.html"&gt;RSSAdder&lt;/a&gt; для полуавтоматического добавления RSS-лент в RSS-агрегаторы.&lt;br /&gt;&lt;br /&gt;Выкладываю новый конфиг. Этот конфиг "увидел свет" благодаря &lt;a href="http://dsi5.org.ua/"&gt;Никите&lt;/a&gt;, который прислал свои исправления (из списка удалено 2 неработающих агрегатора) и добавления (добавлено аж 27 новых агрегаторов!), за что ему &lt;span style="font-weight:bold;"&gt;огромное спасибо&lt;/span&gt; :)&lt;br /&gt;&lt;br /&gt;Сама я новый список еще не тестировала, но, наверное, вскоре пройдусь по нему.&lt;br /&gt;&lt;br /&gt;Ссылка для скачивания: &lt;a href="http://narod.ru/disk/24043375000/RSSAdder.ini.html"&gt;новый конфиг для RSSAdder-а (82 агрегатора)&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Надеюсь, всем пригодится :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-6097858155597548034?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/wpFYBcqrmSQlG6c0DoWMQ7A07kE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wpFYBcqrmSQlG6c0DoWMQ7A07kE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/wpFYBcqrmSQlG6c0DoWMQ7A07kE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wpFYBcqrmSQlG6c0DoWMQ7A07kE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/O8HLAQACXl4" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/08/rssadder.html</link><author>noreply@blogger.com (Masha)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-4871971988147657319</guid><pubDate>Sun, 15 Aug 2010 20:24:00 +0000</pubDate><atom:updated>2010-08-16T00:29:29.661+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">RegExp</category><title>Delphi: работа с RegExp в dll</title><description>&lt;span style="font-weight:bold;"&gt;Как работать с RegExp в библиотеках?&lt;/span&gt; Дело в том, что библиотека &lt;span style="font-weight:bold;"&gt;VBScript_RegExp_55_TLB&lt;/span&gt; — майкрософтская, без проблем тут не обошлось.&lt;br /&gt;&lt;br /&gt;Вроде экземпляр объекта TRegExp создается, но при попытке с ним поработать вылазит ошибка:&lt;br /&gt;&lt;br /&gt;&lt;div class="error"&gt;Не был произведен вызов CoInitialize.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Если честно, столкнулась с этим впервые, пошла в интернет искать. Пришлось подключать &lt;span style="font-weight:bold;"&gt;ActiveX&lt;/span&gt; и использовать &lt;span style="font-weight:bold;"&gt;CoInitialize/CoUninitialize&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Нашла &lt;a href="http://chrisbensen.blogspot.com/2007/06/delphi-tips-and-tricks.html" rel="nofollow"&gt;статью про подобную проблему&lt;/a&gt;. Попробовала так:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var&lt;br /&gt;  ...&lt;br /&gt;  RE                  : TRegExp;&lt;br /&gt;  NeedToUninitialize  : Boolean;&lt;br /&gt;begin&lt;br /&gt;  NeedToUninitialize := Succeeded(CoInitialize(nil));&lt;br /&gt;  try&lt;br /&gt;    RE := TRegExp.Create(nil);&lt;br /&gt;    RE.IgnoreCase := true;&lt;br /&gt;    RE.Multiline := true;&lt;br /&gt;    RE.Global := true;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;  finally&lt;br /&gt;    RE.Free;&lt;br /&gt;    if NeedToUninitialize then CoUninitialize;&lt;br /&gt;  end;&lt;br /&gt;...&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Но и тут не заладилось: если &lt;span style="font-weight:bold;"&gt;CoUninitialize&lt;/span&gt; писать без &lt;span style="font-weight:bold;"&gt;try...except&lt;/span&gt;, то на нем вываливается. И что-то мне эта ситуация совсем не понравилась, как-то мутно: коинициализируется — а потом что? Не будет ли проблем, если работать с библиотекой в несколько потоков и т.д.? Даже если CoInitialize/CoUninitialize делать не в функции, а при регистрации либы.&lt;br /&gt;&lt;br /&gt;Кто-нибудь из читателей блога сталкивался с чем-нибудь подобным? Как решали?&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-4871971988147657319?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4f-IesWjDUB9UyTusij2gg4CxZI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4f-IesWjDUB9UyTusij2gg4CxZI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4f-IesWjDUB9UyTusij2gg4CxZI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4f-IesWjDUB9UyTusij2gg4CxZI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/0eJOby6z8BY" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/08/delphi-regexp-dll.html</link><author>noreply@blogger.com (Masha)</author><thr:total>12</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-459392915106420607</guid><pubDate>Mon, 19 Jul 2010 08:54:00 +0000</pubDate><atom:updated>2010-07-19T13:11:03.301+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Simple HTML DOM</category><title>Навигация по DOM-дереву в html</title><description>Сегодня статья опять будет про &lt;span style="font-weight:bold;"&gt;PHP Simple Html DOM Parser&lt;/span&gt;. Даже несмотря на то, что некоторым читателям эта тема могла хорошенько поднадоесть. :) Просто хочется собрать на блоге достаточное количество материала, к которому можно было бы отсылать вопрошающих по емэйлу.&lt;br /&gt;&lt;br /&gt;Итак, &lt;span style="font-weight:bold;"&gt;навигация по DOM-дереву&lt;/span&gt;. Прямо здесь. Прямо сейчас. На примерах. (Так как теоретически она и так описана в инструкции к библиотеке).&lt;br /&gt;&lt;br /&gt;Если вы читаете эту статью, то вам уже известно, что такое DOM-структура, древовидное представление данных, узлы дерева, родитель, потомок и т.д.. Структуру html-документа в виде дерева можно наглядно посмотреть в Firebug-е.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/TEQTbhEY6fI/AAAAAAAAARY/3OtM4iBlNwU/s1600/html_firebug.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 119px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/TEQTbhEY6fI/AAAAAAAAARY/3OtM4iBlNwU/s400/html_firebug.png" border="0" alt="Древовидная структура html-документа в firebug" title="Древовидная структура html-документа в firebug" id="BLOGGER_PHOTO_ID_5495538808753023474" /&gt;&lt;/a&gt;&lt;br /&gt;Там же есть закладка DOM, с содержимым которой советую ознакомиться новичкам. Из структуры, которая там раскрывается, вы наглядно увидите результаты обращения к дочерним элементам, отдельным узлам, свойствам, атрибутам и т.д..&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/TEQTb_mlnYI/AAAAAAAAARg/09nqvfF4CAc/s1600/dom_firebug.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 119px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/TEQTb_mlnYI/AAAAAAAAARg/09nqvfF4CAc/s400/dom_firebug.png" border="0" alt="DOM-структура html-документа в firebug" title="DOM-структура html-документа в firebug" id="BLOGGER_PHOTO_ID_5495538816949525890" /&gt;&lt;/a&gt;&lt;br /&gt;Но вернемся к PHP Simple Html DOM Parser.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Для примера возьмем простенький html-код с таблицей из трех столбцов и трех строк.&lt;br /&gt;&lt;pre&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;lt;table sellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;tr&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;1.1&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;1.2&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;1.3&amp;lt;/td&amp;gt;&lt;br /&gt;  &amp;lt;/tr&amp;gt;&lt;br /&gt;  &amp;lt;tr&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;2.1&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;2.2&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;2.3&amp;lt;/td&amp;gt;&lt;br /&gt;  &amp;lt;/tr&amp;gt;&lt;br /&gt;  &amp;lt;tr&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;3.1&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;3.2&amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;td&amp;gt;3.3&amp;lt;/td&amp;gt;&lt;br /&gt;  &amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Для тех, кто не любит читать на английском, привожу перевод описания функций:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;mixed $e-&amp;gt;children ( [int $index] )&lt;/span&gt; - возвращает N-ого потомка, если $index указан, или массив всех потомков, если индекс не указан.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;element $e-&amp;gt;parent ()&lt;/span&gt; - возвращает родителя элемента.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;element $e-&amp;gt;first_child ()&lt;/span&gt; - возвращает первого потомка элемента или null, если потомков нет.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;element $e-&amp;gt;last_child ()&lt;/span&gt; - возвращает последнего потомка элемента или null, если потомков нет.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;element $e-&amp;gt;next_sibling ()&lt;/span&gt; - возвращает следующего потомка элемента или null, если таковой не найден.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;element $e-&amp;gt;prev_sibling ()&lt;/span&gt; - возвращает предыдущего потомка элемента или null, если таковой не найден.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;children(N) эквивалентно childNodes(N).&lt;br /&gt;&lt;br /&gt;Следующий код пройдет позволит обойти все ряды и столбцы таблицы:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$html = str_get_html($html_str);&lt;br /&gt;foreach ($html-&amp;gt;find("table", 0)-&amp;gt;children() as $tr) {&lt;br /&gt;  foreach ($tr-&amp;gt;children() as $td) {&lt;br /&gt;    echo  $td-&amp;gt;innertext.';&amp;amp;nbsp;';&lt;br /&gt;  }&lt;br /&gt;  echo '&amp;lt;br&amp;gt;';&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если в переменной $html_str приведенный выше код таблицы, то результат будет:&lt;br /&gt;&lt;pre&gt;1.1; 1.2; 1.3;&lt;br /&gt;2.1; 2.2; 2.3;&lt;br /&gt;3.1; 3.2; 3.3;&lt;/pre&gt;&lt;br /&gt;Тут, думаю, все понятно, проблем возникнуть не должно. Просто проходимся по массивам.&lt;br /&gt;Следующий способ навигации - с помощью next_sibling. next_sibling и prev_sibling используют для навигации по элементам, находящимся на одном уровне (т.е. имеющих общего родителя). Для примера пройдемся по всем ячейкам первой строки таблицы. Код этой нехитрой операции будет выглядеть так:&lt;br /&gt;&lt;pre&gt;$element = $html-&gt;find('table tr td',0);&lt;br /&gt;while($element) {&lt;br /&gt;  echo $element-&amp;gt;innertext.';&amp;amp;nbsp;';&lt;br /&gt;  $element = $element-&amp;gt;next_sibling();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Результатом будет:&lt;br /&gt;&lt;pre&gt;1.1; 1.2; 1.3;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Вот, в принципе, и все. Постаралась коротко и ясно, без всякой воды.&lt;br /&gt;Удачных разработок!&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-459392915106420607?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/p1QHQNJOyjsmuj_2OAENl_JavA8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/p1QHQNJOyjsmuj_2OAENl_JavA8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/p1QHQNJOyjsmuj_2OAENl_JavA8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/p1QHQNJOyjsmuj_2OAENl_JavA8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/RhQZmbYGPX4" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/07/dom-html.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_dS8cdXcqCug/TEQTbhEY6fI/AAAAAAAAARY/3OtM4iBlNwU/s72-c/html_firebug.png" height="72" width="72" /><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-4951304330906720721</guid><pubDate>Thu, 08 Jul 2010 07:58:00 +0000</pubDate><atom:updated>2010-07-28T11:08:51.851+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">компоненты и библиотеки Delphi</category><title>Delphi: отладка, запись в лог</title><description>Давненько не писала про Delphi, а ведь именно в Delphi провожу большую часть дня :) Итак, сегодня расскажу о том, как я пишу логи.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Запись в лог&lt;/span&gt; я использую во всех более-менее серьезных проектах. Логирование помогает и на этапе отладки, и на этапе внедрения (иногда проще попросить прислать лог, чем со слов понять, в чем проблема). Давно уже использую для этих целей маленькую и удобную библиотечку &lt;span style="font-weight:bold;"&gt;uLog&lt;/span&gt;. Все, что от вас потребуется, это добавить ее в uses. Ну и по желанию некоторые настройки. Но даже уже без всяких настроек вы можете писать в лог с помощью процедуры sLog. Пример:&lt;br /&gt;&lt;pre&gt;sLog ('MyProgram.log','Значение переменной ='+str);&lt;/pre&gt;&lt;br /&gt;Первый входной параметр — куда писать, второй — что писать. Если путь прописан не полностью — идет обращение к текущей директории проекта. Если файл не существует - он будет создан автоматически. В логе строчки появляются снабженные временем записи в лог. Пример части лога:&lt;br /&gt;&lt;pre&gt;07.07.2010 16:34:11 [243] Starting...&lt;br /&gt;07.07.2010 16:34:11 [243] Signature:A951D217D6B5E340 03040002 940000000500000001000000280A00000200000053657276696365205061636B2032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&lt;br /&gt;07.07.2010 16:34:11 [243] LoadConf...&lt;/pre&gt;(в квадратных скобках после времени - миллисекунды)&lt;br /&gt;&lt;br /&gt;Немного о настройках. Первое, что можно настраивать, - это файл по умолчанию, в который будет писаться лог. Для этого обращаемся к &lt;span style="font-style:italic;"&gt;uLog.LogFileName&lt;/span&gt;.&lt;br /&gt;Пример:&lt;br /&gt;&lt;pre&gt;uLog.LogFileName := ExtractFilePath(GetModuleName(HInstance))+'TaskManager.log';&lt;/pre&gt;&lt;br /&gt;После установки LogFileName лог будет писаться в указанный файл, если первый входной параметр у sLog не будет задан:&lt;br /&gt;&lt;pre&gt;sLog('','Инициализация прошла успешно');&lt;/pre&gt;&lt;br /&gt;Еще один параметр - &lt;span style="font-style:italic;"&gt;EnableMessages&lt;/span&gt;. Он отвечает за то, будут ли появляться сообщения об ошибке в этой библиотеке (например, когда файл лога не указан и не задан по умолчанию).&lt;br /&gt;&lt;pre&gt;uLog.EnableMessages := false;&lt;/pre&gt;&lt;br /&gt;И, наконец, самый замечательный параметр. Он передается в sLog третьим. Это "уровень логирования", &lt;span style="font-style:italic;"&gt;LogLevel&lt;/span&gt;.&lt;br /&gt;&lt;pre&gt;slog('','Starting library',2);&lt;br /&gt;slog('',tmpS,3);&lt;/pre&gt;&lt;br /&gt;Вы сами проставляете этот уровень в зависимости от того, что за информацию пишете в лог. Градация, например, может быть такой:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;мегаважный&lt;/li&gt;&lt;br /&gt;&lt;li&gt;информационный (запустился, ...)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;отладочный&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Уровней может быть больше, сколько угодно. При приведенном в предыдущем абзаце варианте вы на этапе разработки используете LogLevel := 3, а когда устанавливаете продукт клиенту, ставите LogLevel 1 или 2. LogLevel удобно задавать в ini-файле и считывать при запуске приложения. Если вдруг у клиента внезапно начнутся какие-то сбои - под вашим руководством он сможет поменять уровень логирования на 3 и прислать вам файл с отладочной информацией, которая поможет вам выяснить причину сбоев.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Листинг этой чудесной библиотечки:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;unit uLog;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;  procedure sLog (aLogFileName: String; aMessage : String;&lt;br /&gt;                  aLevel : integer = 1 ;&lt;br /&gt;                  aRewrite : boolean = false ;&lt;br /&gt;                  aIncludeDateTime : boolean=true   ) ;&lt;br /&gt;  var&lt;br /&gt;    LogLevel : integer = 3 ;&lt;br /&gt;    EnableMessages : boolean = true ;&lt;br /&gt;    LogFileName : String ;&lt;br /&gt;    // 1 - мегаважный&lt;br /&gt;    // 2 - информационный (запустился, ...)&lt;br /&gt;    // 3 - отладочный&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;uses SysUtils, Dialogs, DateUtils;&lt;br /&gt;&lt;br /&gt;procedure sLog (aLogFileName: String; aMessage : String;&lt;br /&gt;                  aLevel : integer = 1 ;&lt;br /&gt;                  aRewrite : boolean = false ;&lt;br /&gt;                  aIncludeDateTime : boolean=true   ) ;&lt;br /&gt;  var&lt;br /&gt;   Y,M,D,H,Min,Sec,MSec : word ;&lt;br /&gt;   LF : Text ;&lt;br /&gt;   S : String ;&lt;br /&gt;   S1 : String ;&lt;br /&gt;   N : TDateTime ;&lt;br /&gt;  begin&lt;br /&gt;   {$I-}&lt;br /&gt;   if aLogFileName='' then aLogFileName:=LogFileName ;&lt;br /&gt;   if aLevel&lt;= LogLevel then&lt;br /&gt;    begin&lt;br /&gt;     S:='' ;&lt;br /&gt;     AssignFile(LF,aLogFileName) ;&lt;br /&gt;     if FileExists(aLogFileName) then&lt;br /&gt;      begin&lt;br /&gt;       if aRewrite then Rewrite(LF) else append(LF) ;&lt;br /&gt;       if IOResult&lt;&gt;0 then&lt;br /&gt;        if EnableMessages then&lt;br /&gt;          ShowMessage('error appending message ' + aMessage) ;&lt;br /&gt;      end&lt;br /&gt;     else&lt;br /&gt;      begin&lt;br /&gt;       rewrite(LF) ;&lt;br /&gt;      if IOResult&lt;&gt;0 then&lt;br /&gt;       if EnableMessages then&lt;br /&gt;        ShowMessage('error rewriting message ' + aMessage) ;&lt;br /&gt;      end ;&lt;br /&gt;     if aIncludeDateTime then&lt;br /&gt;      begin&lt;br /&gt;       N:=Now ;&lt;br /&gt;       DecodeDateTime(N,Y,M,D,H,Min,Sec,MSec);&lt;br /&gt;       S1:=' [' + IntToStr(MSec)+ '] ' ;&lt;br /&gt;       while Length(S1) &lt;7 do S1:=S1+' ' ;&lt;br /&gt;       S:=DateTimeToStr(N) + S1 ;&lt;br /&gt;      end;&lt;br /&gt;     S:=S + aMessage ;&lt;br /&gt;     writeln(LF,S) ;&lt;br /&gt;     if IOResult&lt;&gt;0 then&lt;br /&gt;      if EnableMessages then&lt;br /&gt;       ShowMessage('error writing message ' + aMessage) ;&lt;br /&gt;     closeFile(LF) ;&lt;br /&gt;     if IOResult&lt;&gt;0 then&lt;br /&gt;      if EnableMessages then ShowMessage('error closing message ' + aMessage) ;&lt;br /&gt;    end ;&lt;br /&gt;   {$I+}&lt;br /&gt;  end ;&lt;br /&gt;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Добавлю ссылку на &lt;a href="http://delphiday.blogspot.com/2010/07/tforgirlsdebug.html"&gt;класс&lt;/a&gt;, который написал &lt;a href="http://delphiday.blogspot.com/"&gt;Егор&lt;/a&gt;. С помощью класса можно посмотреть дамп любой области памяти и актуальный стекфлоу. Спасибо ему большое!&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-4951304330906720721?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/rxd7upOpKwxLGcs9x-F4D0U2cMY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rxd7upOpKwxLGcs9x-F4D0U2cMY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/rxd7upOpKwxLGcs9x-F4D0U2cMY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rxd7upOpKwxLGcs9x-F4D0U2cMY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/xqffnCyJmis" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/07/delphi.html</link><author>noreply@blogger.com (Masha)</author><thr:total>14</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-4979275016597151313</guid><pubDate>Fri, 25 Jun 2010 21:10:00 +0000</pubDate><atom:updated>2010-06-26T01:27:35.103+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">регулярные выражения</category><category domain="http://www.blogger.com/atom/ns#">парсинг</category><title>Очистка текста от лишних html-тегов</title><description>С задачей очистки html от лишних тегов сталкиваются абсолютно все.&lt;br /&gt;&lt;br /&gt;Первое, что приходит на ум, это использовать php-функцию strip_tags():&lt;br /&gt;&lt;pre&gt;string strip_tags (string str [, string allowable_tags])&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Функция возвращает строку, очищенную от тегов. В качестве аргумента &lt;span style="font-weight:bold;"&gt;allowable_tags&lt;/span&gt; передаются теги, которые &lt;span style="font-weight:bold;"&gt;не надо удалять&lt;/span&gt;. Функция работает, но, мягко говоря, неидеально. По ходу, там нет проверки на валидность кода, что может повлечь за собой удаление текста, не входящего в тэги.&lt;br /&gt;Инициативные разработчики сложа руки не сидели — в сети можно найти доработанные функции. Хорошим примером является &lt;a href="http://forum.dklab.ru/viewtopic.php?p=153841" rel="nofollow"&gt;strip_tags_smart&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Применять или не применять готовые решения — личный выбор программиста. Так сложилось, что мне чаще всего не требуется "универсального" обработчика и бывает удобнее почистить код регулярками.&lt;br /&gt;&lt;br /&gt;От чего зависит выбор того или иного способа обработки?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;1. От исходного материала и сложности его анализа.&lt;/span&gt;&lt;br /&gt;Если вам нужно обрабатывать достаточно простые htmp-тексты, без какой-либо навороченной верстки, ясные, как день :), то можно использовать стандартные функции.&lt;br /&gt;Если в текстах есть определенные особенности, которые надо учесть, то тут-то и пишутся специальные обработчики. В одних может использоваться просто &lt;span style="font-weight:bold;"&gt;str_replace&lt;/span&gt;. Например:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$s = array(&amp;#39;&amp;amp;acirc;&amp;amp;euro;&amp;amp;trade;&amp;#39; =&amp;gt; &amp;#39;&amp;amp;rsquo;&amp;#39;,         // Right-apostrophe (eg in I&amp;#39;m)&lt;br /&gt;  &amp;#39;&amp;amp;acirc;&amp;amp;euro;&amp;amp;oelig;&amp;#39; =&amp;gt; &amp;#39;&amp;amp;ldquo;&amp;#39;,                  // Opening speech mark&lt;br /&gt;  &amp;#39;&amp;amp;acirc;&amp;amp;euro;&amp;amp;ldquo;&amp;#39; =&amp;gt; &amp;#39;&amp;amp;mdash;&amp;#39;,                  // Long dash&lt;br /&gt;  &amp;#39;&amp;amp;acirc;&amp;amp;euro;&amp;#39; =&amp;gt; &amp;#39;&amp;amp;rdquo;&amp;#39;,                         // Closing speech mark&lt;br /&gt;  &amp;#39;&amp;amp;Atilde;&amp;amp;copy;&amp;#39; =&amp;gt; &amp;#39;&amp;amp;eacute;&amp;#39;,                       // e acute accent&lt;br /&gt;  chr(226) . chr(128) . chr(153) =&amp;gt; &amp;#39;&amp;amp;rsquo;&amp;#39;,          // Right-apostrophe again&lt;br /&gt;  chr(226) . chr(128) . chr(147) =&amp;gt; &amp;#39;&amp;amp;mdash;&amp;#39;,          // Long dash again&lt;br /&gt;  chr(226) . chr(128) . chr(156) =&amp;gt; &amp;#39;&amp;amp;ldquo;&amp;#39;,          // Opening speech mark&lt;br /&gt;  chr(226) . chr(128) . chr(148) =&amp;gt; &amp;#39;&amp;amp;mdash;&amp;#39;,          // M dash again&lt;br /&gt;  chr(226) . chr(128) =&amp;gt; &amp;#39;&amp;amp;rdquo;&amp;#39;,                     // Right speech mark&lt;br /&gt;  chr(195) . chr(169) =&amp;gt; &amp;#39;&amp;amp;eacute;&amp;#39;,                    // e acute again&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;foreach ($s as $needle =&gt; $replace)&lt;br /&gt;  {&lt;br /&gt;    $htmlText = str_replace($needle, $replace, $htmlText);&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Другие могут быть основаны на &lt;span style="font-weight:bold;"&gt;регулярных выражениях&lt;/span&gt;. Как пример:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function getTextFromHTML($htmlText)&lt;br /&gt;{&lt;br /&gt;    $search = array (&amp;quot;&amp;#39;&amp;lt;script[^&amp;gt;]*?&amp;gt;.*?&amp;lt;/script&amp;gt;&amp;#39;si&amp;quot;,  // Remove javaScript &lt;br /&gt;       &amp;quot;&amp;#39;&amp;lt;style[^&amp;gt;]*?&amp;gt;.*?&amp;lt;/style&amp;gt;&amp;#39;si&amp;quot;,  // Remove styles &lt;br /&gt;       &amp;quot;&amp;#39;&amp;lt;xml[^&amp;gt;]*?&amp;gt;.*?&amp;lt;/xml&amp;gt;&amp;#39;si&amp;quot;,  // Remove xml tags &lt;br /&gt;       &amp;quot;&amp;#39;&amp;lt;[\/\!]*?[^&amp;lt;&amp;gt;]*?&amp;gt;&amp;#39;si&amp;quot;,           // Remove HTML-tags &lt;br /&gt;       &amp;quot;&amp;#39;([\r\n])[\s] &amp;#39;&amp;quot;,                 // Remove spaces&lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(quot|#34);&amp;#39;i&amp;quot;,                 // Replace HTML special chars&lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(amp|#38);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(lt|#60);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(gt|#62);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(nbsp|#160);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(iexcl|#161);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(cent|#162);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(pound|#163);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;(copy|#169);&amp;#39;i&amp;quot;, &lt;br /&gt;       &amp;quot;&amp;#39;&amp;amp;#(\d );&amp;#39;e&amp;quot;);                    // write as php&lt;br /&gt;&lt;br /&gt;    $replace = array (&amp;quot;&amp;quot;, &lt;br /&gt;                      &amp;quot;&amp;quot;, &lt;br /&gt;                      &amp;quot;&amp;quot;,&lt;br /&gt;                      &amp;quot;&amp;quot;,&lt;br /&gt;                      &amp;quot;\\1&amp;quot;, &lt;br /&gt;                      &amp;quot;\&amp;quot;&amp;quot;, &lt;br /&gt;                      &amp;quot;&amp;amp;&amp;quot;, &lt;br /&gt;                      &amp;quot;&amp;lt;&amp;quot;, &lt;br /&gt;                      &amp;quot;&amp;gt;&amp;quot;, &lt;br /&gt;                      &amp;quot; &amp;quot;, &lt;br /&gt;                      chr(161), &lt;br /&gt;                      chr(162), &lt;br /&gt;                      chr(163), &lt;br /&gt;                      chr(169), &lt;br /&gt;                      &amp;quot;chr(\\1)&amp;quot;); &lt;br /&gt;                      &lt;br /&gt;    return preg_replace($search, $replace, $htmlText);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;(В такие минуты как никогда радует возможность preg_replace работать с массивами в качестве параметров). Массив вы при необходимости дополняете своими регулярками. Помочь в их составлении вам может, например, этот &lt;a href="http://realcode.ru/regexptester/"&gt;конструктор регулярных выражений&lt;/a&gt;. Начинающим разработчикам может быть полезной статья &lt;a href="http://www.pagecolumn.com/tool/all_about_html_tags.htm" rel="nofollow"&gt;"All about HTML tags. 9 Regular Expressions to strip HTML tags"&lt;/a&gt;. Посмотрите там примеры, проанализируйте логику.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;2. От объемов.&lt;/span&gt;&lt;br /&gt;Объемы напрямую связаны со сложностью анализа (из предыдущего пункта). Большое количество текстов увеличивает вероятность, что, пытаясь предусмотреть и почистить все регулярками, вы можете что-нибудь да упустить. В этом случае подойдет метод "многоступенчатой" очистки. То есть очистить сначала, допустим, функцией strip_tags_smart (исходники на всякий случай не удаляем). Потом выборочно просматриваем некоторое количество текстов на выявление "аномалий". Ну и "зачищаем" аномалии регулярками.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;3. От того, что надо получить в результате.&lt;/span&gt;&lt;br /&gt;Алгоритм обработки может быть упрощен разными способами в зависимости от ситуации. Случай, описанный мной в &lt;a href="http://parsing-and-i.blogspot.com/2010/06/wordpress-php-simple-html-dom-parser.html"&gt;одной из предыдущих статей&lt;/a&gt;, хорошо это демонстрирует. Напомню, текст там находился в div-е, в котором кроме него был еще div с "хлебными крошками", реклама адсенс, список похожих статей. При анализе выборки статей обнаружилось, что статьи не содержат рисунков и просто разбиты на абзацы с помощью &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;. Чтобы не чистить "главный" див от посторонних вещей, можно найти все абзацы (с Simple HTML DOM Parser это очень просто) и соединить их содержимое. Так что прежде чем составлять регулярки для чистки, посмотрите, нельзя ли обойтись малой кровью.&lt;br /&gt;&lt;br /&gt;Вообще, между сторонниками парсинга html-кода, основанного чисто на регулярных выражениях, и парсинга, в основе которого лежит анализ DOM-структуры документа, в сети разгораются настоящие холивары. Вот, например, на оверфлоу. Невинный с первого взгляда &lt;a href="http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/" rel="nofollow"&gt;вопрос&lt;/a&gt; вызвал очень бурное обсуждение (особенно обратите внимание на первый коммент, за который проголосовало уже более 3 тысяч человек, — чувак отжег :) ).&lt;br /&gt;В общем, каждый выбирает то, что ему ближе и что лучше подходит для конкретной ситуации.&lt;br /&gt;&lt;br /&gt;А какой способ предпочитаете вы?&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-4979275016597151313?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ueb_xJ3hTV78Xqeyped6Re5rRYU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ueb_xJ3hTV78Xqeyped6Re5rRYU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ueb_xJ3hTV78Xqeyped6Re5rRYU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ueb_xJ3hTV78Xqeyped6Re5rRYU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/tPYYz2IDl0s" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/06/html.html</link><author>noreply@blogger.com (Masha)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-7143260105067710426</guid><pubDate>Wed, 23 Jun 2010 12:08:00 +0000</pubDate><atom:updated>2010-06-23T16:27:08.287+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">Simple HTML DOM</category><title>3 способа установки User Agent при работе с библиотекой Simple HTML DOM Parser</title><description>Про &lt;a href="http://parsing-and-i.blogspot.com/2010/06/user-agent.html"&gt;юзерагенты&lt;/a&gt; на этом блоге я уже рассказывала. И говорила, что "неподставление" данных о User Agent-е в заголовок вашего запроса может выйти вам боком. Как подставить данные, если вы пользуетесь библиотекой &lt;a href="http://parsing-and-i.blogspot.com/2010/05/php-simple-html-dom-parser.html"&gt;Simple HTML DOM Parser&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;Есть несколько очевидных способов.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Способ 1.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Используйте Simple HTML DOM Parser в связке с cURL.&lt;/span&gt;&lt;br /&gt;Самый простой способ. И для меня — самый удобный. Для установки юзерагента используйте параметр &lt;span style="font-style:italic;"&gt;CURLOPT_USERAGENT&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ch = curl_init();&lt;br /&gt;curl_setopt($ch, CURLOPT_URL, $url);&lt;br /&gt;curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);&lt;br /&gt;curl_setopt($cr, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)');&lt;br /&gt;curl_setopt($ch, CURLOPT_TIMEOUT,5); &lt;br /&gt;curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);&lt;br /&gt;&lt;br /&gt;$html_curl = curl_exec($ch); &lt;br /&gt;curl_close($ch); &lt;br /&gt;&lt;br /&gt;$html = str_get_html($html_curl);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Способ 2.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Можете установить браузер по умолчанию в &lt;a href="http://www.php.net/manual/en/filesystem.configuration.php#ini.user-agent" rel="nofollow"&gt;php.ini&lt;/a&gt; или использовать ini_set().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Пример:&lt;br /&gt;&lt;pre&gt;ini_set("user_agent","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Тоже достаточно простой и удобный способ. Первый я в основном использую из-за того, что, как правило, приходится работать с cURL-ом (и с его возможностями) при загрузке страниц, поэтому мне удобнее установить User Agent именно там. Если бы курл был не нужен - использовала бы ini_set.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Способ 3.&lt;/span&gt; &lt;span style="font-weight:bold;"&gt;Можете непосредственно внести изменения в функцию библиотеки load_file().&lt;/span&gt;&lt;br /&gt;Я сама этот способ не пробовала, но в архивах на всякий случай записан этот способ, взятый с стэковерфлоу.&lt;br /&gt;&lt;br /&gt;В исходном коде функция выглядит следующим образом:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// load html from file&lt;br /&gt;function load_file() {&lt;br /&gt;  $args = func_get_args();&lt;br /&gt;  $this-&gt;load(call_user_func_array('file_get_contents', $args), true);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;А ее модификация может быть, например, такой:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// load html from file&lt;br /&gt;function load_file() {&lt;br /&gt;  $args = func_get_args();&lt;br /&gt;  // Added by Mithun&lt;br /&gt;  $opts = array(&lt;br /&gt;          'http'=&gt;array(&lt;br /&gt;                 'method'=&gt;"GET",&lt;br /&gt;                 'header'=&gt;"Accept-language: en\r\n" .&lt;br /&gt;                 "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n".&lt;br /&gt;                 "Cookie: foo=bar\r\n"&lt;br /&gt;                )&lt;br /&gt;        );&lt;br /&gt;  $context = stream_context_create($opts);&lt;br /&gt;  $args[1] = FALSE;&lt;br /&gt;  $args[2] = $context;&lt;br /&gt;  // End Mithun&lt;br /&gt;  $this-&gt;load(call_user_func_array('file_get_contents', $args), true);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Еще раз напомню, для чего вам User Agent. Допустим, вы пишете парсер, краулер или бота. Некоторые вебмастеры в robots.txt запрещают доступ к материалам сайта, если в хэдэре запроса установлен User Agent, стандартный для библиотек, работающих с http (или информация о нем вообще отсутствует). Устанавливая User Agent, вы маскируете свой запрос так, словно он поступил от браузера.&lt;br /&gt;&lt;br /&gt;Но в любом случае надо знать меру. Если вы генерируете сотни запросов в минуту, то никакой юзерагент вас не прикроет :)&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-7143260105067710426?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/e40u48_k5zv44YghQzv48P81zRM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/e40u48_k5zv44YghQzv48P81zRM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/e40u48_k5zv44YghQzv48P81zRM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/e40u48_k5zv44YghQzv48P81zRM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/P5hn8GURPyY" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/06/3-user-agent-simple-html-dom-parser.html</link><author>noreply@blogger.com (Masha)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-1895380938298802497</guid><pubDate>Tue, 15 Jun 2010 10:05:00 +0000</pubDate><atom:updated>2010-06-15T14:37:55.885+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">Simple HTML DOM</category><category domain="http://www.blogger.com/atom/ns#">парсер</category><title>Разработка парсера каталога статей на движке WordPress с использованием PHP Simple HTML DOM Parser. Пошаговая инструкция</title><description>Всем доброго дня! Сегодня я опять отвечу на вопрос читателя блога. Вопрос был задан еще до объявления об Акции "Разобрать на моем примере", тем не менее:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Маша, помогите пожалуйста спарсить http://articlet.com/, точнее расскажите что нужно делать пошагово.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Руководство по мере написания вылилось в достаточно длинную статью, но я решила не разбивать ее на части.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Изучение ресурса и разработка алгоритма&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Итак, первое, что мы сделаем, — посмотрим, что из себя представляет ресурс. Это &lt;span style="font-weight:bold;"&gt;каталог статей&lt;/span&gt;, сделанный на &lt;span style="font-weight:bold;"&gt;WordPress&lt;/span&gt;. Структура рубрикатора — двухуровневая. То есть в корне находится несколько разделов, в каждом из которых могут находиться подразделы. Доступ к подразделам можно осуществить и с главной страницы и с внутренней страницы раздела. Нам, естественно, удобнее собрать все с главной.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_dS8cdXcqCug/TBdRVOyYcMI/AAAAAAAAAQw/_qXXiLhbA4c/s1600/screen1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 291px;" src="http://2.bp.blogspot.com/_dS8cdXcqCug/TBdRVOyYcMI/AAAAAAAAAQw/_qXXiLhbA4c/s400/screen1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482940496535384258" /&gt;&lt;/a&gt;&lt;br /&gt;Статьи могут находиться как в главных разделах, так и в подразделах.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/TBdRVYCiaLI/AAAAAAAAAQ4/l45RLcXI32g/s1600/screen2_subs.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 233px; height: 265px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/TBdRVYCiaLI/AAAAAAAAAQ4/l45RLcXI32g/s400/screen2_subs.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482940499019065522" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Количество статей в подразделах указано в скобках после названия раздела. Так что можно проверить его, и, если оно равно 0, - не заходить. А можно не заморачиваться, заходить везде и на месте уже проверять, есть статьи или их нет.&lt;br /&gt;&lt;br /&gt;В разделах есть пагинация. Вообще, сам сайт очень маленький, я не понимаю, почему попросили разобрать пример именно на нем. Разделов с пагинацией очень мало.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/TBdRVmET5FI/AAAAAAAAARA/saXjBJYAyu0/s1600/screen3_pages.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 221px; height: 62px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/TBdRVmET5FI/AAAAAAAAARA/saXjBJYAyu0/s400/screen3_pages.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482940502784599122" /&gt;&lt;/a&gt;&lt;br /&gt;Ссылка на страницы формируется как:&lt;br /&gt;&lt;pre&gt;{ссылка на категорию}page/{номер страницы}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Определяемся с &lt;span style="font-weight:bold;"&gt;алгоритмом обхода&lt;/span&gt;: сначала парсим все названия и ссылки на разделы/категории с главной, потом заходим по всем полученным ссылкам, там парсим названия и ссылки всех статей, парсим содержимое этих статей (если выдача разбита на страницы - проходим по страницам).&lt;br /&gt;&lt;br /&gt;Определяемся с тем, &lt;span style="font-weight:bold;"&gt;в каком формате сохраняем данные&lt;/span&gt;. В прошлый раз, разбирая пример парсинга каталога статей findarticles.com (&lt;a href="http://parsing-and-i.blogspot.com/2009/11/findarticlescom-part-1.html"&gt;часть 1&lt;/a&gt;, &lt;a href="http://parsing-and-i.blogspot.com/2009/12/findarticlescom-part-2.html"&gt;часть 2&lt;/a&gt;), я приводила пример, как сохранять тексты в файлы. В этот раз будем сохранять в простейшую базу данных. Нам понадобится 2 таблицы - таблица рубрик и таблица статей.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CREATE TABLE `article` (&lt;br /&gt;  `n` int(11) NOT NULL auto_increment,&lt;br /&gt;  `rubrika_n` int(11) NOT NULL,&lt;br /&gt;  `title` varchar(255),&lt;br /&gt;  `link` varchar(255),&lt;br /&gt;  `content` text,&lt;br /&gt;  PRIMARY KEY  (`n`)&lt;br /&gt;) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;&lt;br /&gt;CREATE TABLE `rubrika` (&lt;br /&gt;  `n` int(11) NOT NULL auto_increment,&lt;br /&gt;  `up` int(11),&lt;br /&gt;  `name` varchar(255),&lt;br /&gt;  `link` varchar(255),&lt;br /&gt;  PRIMARY KEY  (`n`)&lt;br /&gt;) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Реализация алгоритма&lt;/h2&gt;&lt;br /&gt;&lt;h3&gt;Инструмент&lt;/h3&gt;&lt;br /&gt;Будем использовать библиотеку &lt;a href="http://parsing-and-i.blogspot.com/2010/05/php-simple-html-dom-parser.html"&gt;PHP Simple HTML DOM Parser&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Как я уже писала, на первом этапе просто спарсим все ссылки на разделы/категории. Для этого достаточно внимательно посмотреть на код страницы, понять структуру и задать "путь" для поиска нужных нам элементов.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/TBdRV0o-6uI/AAAAAAAAARI/ZM3FnhMPxQI/s1600/screen4_code.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 288px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/TBdRV0o-6uI/AAAAAAAAARI/ZM3FnhMPxQI/s400/screen4_code.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482940506696510178" /&gt;&lt;/a&gt;&lt;br /&gt;Обычно процесс написания парсеров проходит постепенно и отлаживается поэтапно. Например, на первом этапе для получения ссылок разделов и категорий я получила такой кусок кода (код рабочий, можете запустить и посмотреть).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;include_once('simple_html_dom.php');&lt;br /&gt;&lt;br /&gt;$html = file_get_html('http://articlet.com/');&lt;br /&gt;$cat_n = 1;&lt;br /&gt;foreach($html-&amp;gt;find('div[id="categories"] ul li[id]') as $li) &lt;br /&gt;{&lt;br /&gt;  // находим ссылку на раздел&lt;br /&gt;  $a = $li-&amp;gt;find('div a',0);&lt;br /&gt;  if (isset($a)) {&lt;br /&gt;    echo 'n='.$cat_n.' '.$a-&amp;gt;href.' '.$a-&amp;gt;innertext.'&amp;lt;br&amp;gt;';&lt;br /&gt;    $up=$cat_n;&lt;br /&gt;    $cat_n++;&lt;br /&gt;    &lt;br /&gt;    // находим дочерние категории         &lt;br /&gt;    foreach ($li-&amp;gt;find('ul li a') as $sub_a)&lt;br /&gt;      {&lt;br /&gt;       echo 'up='.$up.' '.$sub_a-&amp;gt;href.' '.$sub_a-&amp;gt;innertext.'&amp;lt;br&amp;gt;';&lt;br /&gt;       $cat_n++;&lt;br /&gt;      }&lt;br /&gt;  }           &lt;br /&gt;}&lt;br /&gt;$html-&amp;gt;clear();&lt;br /&gt;?&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Сайт-источник небольшой, поэтому я не буду разбивать работу парсера на части, а сделаю все в одном скрипте. После получения списка ссылок на категории, пройдемся по ним и соберем нужную нам информацию.&lt;br /&gt;&lt;br /&gt;На этапе парсинга текста статей тоже не обойтись без "тестовых заходов". У сайтов на WordPress-е структура шаблонов похожая. Но весь div с классом post нам здесь не нужен, в нем много лишнего (информация о принадлежности к разделу, реклама гугля и связанные статьи). Поэтому от общего случая перейду к частному: так как картинок там в статьях все равно нет, то в качестве контента для записи в базу буду брать все абзацы (заключенные между &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;). То есть foreach-ем надо пройтись по div[class=post] div[!class] p. div[!class] нужен для того, чтобы взять именно див с контентом, а не с заголовком, у которого атрибут class присутствует.&lt;br /&gt;&lt;br /&gt;Следующее, на что надо обратить внимание, это на всякие символы, которые желательно заменить перед вставкой в базу. Есть одинарные символы, а есть целые последовательности. Например, WP автоматически заменяет кавычки. После скачивания страницы вы увидите вместо кавычек непонятные символы. Чтобы это предотвратить, смотрим в коде символы. Я это, как всегда, делаю FAR-ом. После нажатия F3 и F4 находим по тексту ключевое слово, возле которого обнаружились "кракозябры" и смотрим последовательность.&lt;br /&gt;&lt;br /&gt;Для открывающей кавычки это будет &lt;span style="font-weight:bold;"&gt;E2 80 9C&lt;/span&gt; (или в десятичных кодах - 226 128 156)&lt;br /&gt;для закрывающей — &lt;span style="font-weight:bold;"&gt;E2 80 9D&lt;/span&gt; (226 128 157)&lt;br /&gt;для &amp;mdash; — &lt;span style="font-weight:bold;"&gt;E2 80 93&lt;/span&gt; (226 128 147)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_dS8cdXcqCug/TBdRWBbaiPI/AAAAAAAAARQ/3cBSw6rofPw/s1600/screen5_far.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 103px;" src="http://4.bp.blogspot.com/_dS8cdXcqCug/TBdRWBbaiPI/AAAAAAAAARQ/3cBSw6rofPw/s400/screen5_far.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5482940510129260786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Их всех можно заменить на то, что вам угодно: или на escape-последовательности или на какие-нибудь простые символы.&lt;br /&gt;Также можно заодним избавиться от лишних тегов (в частности - от ссылок).&lt;br /&gt;&lt;br /&gt;С парсингом текста вроде понятно. Листинг кода не привожу, так как приложу к статье исходник полностью.&lt;br /&gt;&lt;br /&gt;Что касается перемещения по страницам, тут можно опять же пойти двумя путями:&lt;br /&gt;1. Искать, есть ли ссылка на следующую страницу. И если есть — переходить.&lt;br /&gt;2. Переходить в любом случае, используя шаблон. Если на той странице ничего нет - останавливаться и переходить к следующей категории.&lt;br /&gt;Первый вариант, безусловно, правильнее. Особенно его "правильность" ощущается на ресурсозатратах при парсинге больших каталогов с сотнями категорий. Но в этом примере я упрощу себе работу и сделаю вторым способом.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$i=0;&lt;br /&gt;while ($i &amp;lt; count($cat_array)) {&lt;br /&gt;  $cat_link = $cat_array[$i];&lt;br /&gt;  $i++;&lt;br /&gt;  $need_next_page = true;&lt;br /&gt;  $page_cnt = 1;&lt;br /&gt;  while ($need_next_page) {&lt;br /&gt;    if ($page_cnt &amp;gt; 1) $url= $cat_link.'/page/'.$page_cnt.'/';&lt;br /&gt;      else $url=$cat_link;&lt;br /&gt;    $cat_html = file_get_html($url);&lt;br /&gt;    $art_cnt=0; // счетчик статей на странице&lt;br /&gt;    foreach($cat_html-&amp;gt;find('div[class=post] h3 a') as $article_a) {&lt;br /&gt;      $article_name = $article_a-&amp;gt;innertext;&lt;br /&gt;      $article_link = $article_a-&amp;gt;href;&lt;br /&gt;      $article_content = parse_article($article_link);&lt;br /&gt;      // добавляем в базу&lt;br /&gt;      $ins_q = 'insert into article (rubrika_n, title, link, content) '.&lt;br /&gt;             'values ('.$i.',\''.addslashes(getTextFromHTML($article_name)).'\',\''.addslashes($article_link).'\',\''.addslashes(getTextFromHTML($article_content)).'\')';&lt;br /&gt;      mysql_query($ins_q);&lt;br /&gt;      $art_cnt++;&lt;br /&gt;      sleep(1); // поставлю секундную задержку на всякий пожарный&lt;br /&gt;    }&lt;br /&gt;    $cat_html-&amp;gt;clear();&lt;br /&gt;    &lt;br /&gt;    // выводится по 20 статей на страницу. Если статей на странице меньше 20,&lt;br /&gt;    // то дальше можно не смотреть&lt;br /&gt;    if ($art_cnt &amp;lt; 20) $need_next_page = false;&lt;br /&gt;      else $page_cnt++; // инкрементируем счетчик страниц   &lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;parse_article (подробнее - в исходниках):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function parse_article ($article_link) {&lt;br /&gt;  $arc_html = file_get_html($article_link);&lt;br /&gt;  $content = '';&lt;br /&gt;  foreach ( $arc_html-&amp;gt;find('div[class=post] div[!class] p') as $p) {&lt;br /&gt;    $content = $content. $p-&amp;gt;innertext."&amp;lt;br&amp;gt;";&lt;br /&gt;    }&lt;br /&gt;  $arc_html-&gt;clear();&lt;br /&gt;  return $content;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;$cat_array&lt;/span&gt; - временный массив, в который на первом этапе записала все ссылки на категории, чтобы лишний раз не обращаться к базе данных. Сделала цикл с while, а не foreach по привычке, чтобы в случае форсмажора можно было начать не с первой категории, а с любой, с какой захочу.&lt;br /&gt;&lt;br /&gt;Видно, что в результате код всего парсера получается вовсе даже не объемный. Можно &lt;a href="http://narod.ru/disk/21869783000/articlet_com_parser.zip.html"&gt;скачать исходник парсера&lt;/a&gt;. Код, конечно, неидеален, можно еще повылизывать его до блеска, но я перед собой такой задачи не ставила.&lt;br /&gt;&lt;br /&gt;В качестве приятного бонуса выкладываю скачанную таким образом базу статей из того каталога (1,883 штук в 189 категориях) в разделе &lt;a href="http://parsing-and-i.blogspot.com/p/free.html"&gt;Халява&lt;/a&gt;. Каталог фришный, ничьих прав мы таким образом не нарушим.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Буду благодарна за ретвит! (Постепенно апгрейжу свой бложик, приделала вот кнопку от tweetmeme :) ).&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-1895380938298802497?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/O_ognau2fRjfZTb-aX5O9cpg_nI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/O_ognau2fRjfZTb-aX5O9cpg_nI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/O_ognau2fRjfZTb-aX5O9cpg_nI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/O_ognau2fRjfZTb-aX5O9cpg_nI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/WZosfPKmHDs" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/06/wordpress-php-simple-html-dom-parser.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_dS8cdXcqCug/TBdRVOyYcMI/AAAAAAAAAQw/_qXXiLhbA4c/s72-c/screen1.png" height="72" width="72" /><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-5048388165526054053</guid><pubDate>Thu, 10 Jun 2010 09:08:00 +0000</pubDate><atom:updated>2010-06-11T10:54:35.862+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">на примерах читателей</category><title>Delphi: пример поиска в структуре веб-страницы значения нужного элемента</title><description>&lt;div class="info"&gt;Привет! Этот пост — ответ на вопрос читателя блога. Конкретный ответ на конкретный вопрос. Именно им я хочу положить начало &lt;span style="font-weight:bold;"&gt;акции "Разберу на вашем примере"&lt;/span&gt;. Подробнее об акции и ее условиях читайте в конце этой статьи.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Цитата из письма:&lt;blockquote&gt;&lt;br /&gt;Мне нужно с сайта http://stock.rbc.ru/demo/micex.0/intraday/eod.rus.shtml скачать хоть одну ячейку например: цену открытия акций Газпрома, и сохранить это значение в какую-нибудь БД или таблицу (например в таблицу PARADOX7) или в EXCEL (лучше PARADOX7). И все, мне больше ничего не требуется, Ваша программа будет служить мне эталонным примером, а дальше я сам разберусь.&lt;br /&gt;...&lt;br /&gt;Я пытаюсь работать в среде С++Builder6 и пробовал использовать компонент XMLDocument, но кроме того как вытащить его на форму ничего не могу с ним сделать. Буду рад если вы напишите мне на С++Builder, но насколько я знаю, что С++Builder и DELPHI имеют множество сходств то и программе написанной на DELPHI буду благодарен...&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Случай сложен тем, что, как мне кажется, если человек не программист, то ему исходный код на другом языке вряд ли поможет. А пример "сохранения в таблицу" не научит работе с базами данных. На скорую руку набросала поиск нужного элемента на странице, постаралась прокомментировать по-максимуму, так что дополнительно сказать нечего.&lt;br /&gt;&lt;br /&gt;Итак, код для поиска элемента:&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;uses&lt;br /&gt;  ...&lt;br /&gt;  UrlMon, MSHTML, activex;&lt;br /&gt;&lt;br /&gt;function DownloadFile(SourceFile, DestFile: string): Boolean;&lt;br /&gt;begin&lt;br /&gt;  try&lt;br /&gt;    Result := UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil) = 0;&lt;br /&gt;  except&lt;br /&gt;    Result := False;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TMainF.ButtonClick(Sender: TObject);&lt;br /&gt;var&lt;br /&gt;  StrURL,&lt;br /&gt;  sFileName   : string;&lt;br /&gt;  SLBody      : TStringList;&lt;br /&gt;  Doc         : IHTMLDocument2;&lt;br /&gt;  v           : OleVariant;&lt;br /&gt;  DocAll,&lt;br /&gt;  DocTR,&lt;br /&gt;  DocTD       : IHTMLElementCollection;&lt;br /&gt;  TRElement,&lt;br /&gt;  TDElement   : IHtmlElement;&lt;br /&gt;  i           : integer;&lt;br /&gt;begin&lt;br /&gt;  // сначала загружаем страницу. это можно сделать разными способами&lt;br /&gt;  // я выбрала загрузку страницы в файл, так как у нас могут быть&lt;br /&gt;  // разные версии компонентов Indy, они могут конфликтовать&lt;br /&gt;&lt;br /&gt;  StrURL := 'http://stock.rbc.ru/demo/micex.0/intraday/eod.rus.shtml';&lt;br /&gt;&lt;br /&gt;  SLBody:=TStringList.Create;&lt;br /&gt;  sFileName := ExtractFilePath(Application.ExeName) + 'cache.txt';&lt;br /&gt;  if DownloadFile(StrURL,sFileName) then&lt;br /&gt;  SLBody.LoadFromFile(sFileName);&lt;br /&gt;  DeleteFile(sFileName);&lt;br /&gt;&lt;br /&gt;  // загружаем все в "WebBrowser". Вернее, создаем объект IHTMLDocument2&lt;br /&gt;  // Объясню, зачем. Дело в том, что структура конкретной&lt;br /&gt;  // интернет-страницы достаточно сложная. Поэтому компонент XMLDocument&lt;br /&gt;  // для ее анализа будет не совсем удобным. Как неудобными будут и &lt;br /&gt;  // регулярные выражения (ведь наверняка нужно будет работать не только &lt;br /&gt;  // с газпромом, а, допустим, пройтись по всей таблице)&lt;br /&gt;  Doc := coHTMLDocument.Create as IHTMLDocument2;&lt;br /&gt;  if Doc = nil then&lt;br /&gt;    begin&lt;br /&gt;      ShowMessage('Ошибка создания IHTMLDocument2');&lt;br /&gt;      exit;&lt;br /&gt;    end;&lt;br /&gt;  v := VarArrayCreate([0,0],VarVariant);&lt;br /&gt;  v[0] := SLBody.Text;&lt;br /&gt;  Doc.write(PSafeArray(TVarData(v).VArray));&lt;br /&gt;&lt;br /&gt;  // ищем в DOM-структуре элемент, значение которого нам нужно.&lt;br /&gt;  // для этого сначала анализируем страницу, чтобы определить признаки,&lt;br /&gt;  // которые нам помогут это сделать. Вот кусок кода:&lt;br /&gt;  // &amp;lt;TR&amp;gt;&lt;br /&gt;  // &amp;lt;TD&amp;gt;&amp;lt;A TARGET=&amp;quot;_top&amp;quot; HREF=&amp;quot;../daily/GAZP.rus.shtml?show=all&amp;quot; class=&amp;quot;n&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;http://pics.rbc.ru/img/up_grf.gif&amp;quot; width=&amp;quot;19&amp;quot; height=&amp;quot;15&amp;quot; border=&amp;quot;0&amp;quot;&amp;gt;  %u0413%u0430%u0437%u043F%u0440%u043E%u043C&amp;lt;/A&amp;gt;&amp;lt;/TD&amp;gt;&lt;br /&gt;  // &amp;lt;TD class=G&amp;gt;158.2&amp;lt;/TD&amp;gt;&lt;br /&gt;  // то есть надо найти элемент TR, в первом TD которого написано "Газпром",&lt;br /&gt;  // и взять значение из второго  TD&lt;br /&gt;&lt;br /&gt;  DocAll := Doc.all;&lt;br /&gt;  DocTR  := DocAll.Tags('TR') as IHTMLElementCollection;&lt;br /&gt;  for i := 0 to DocTR.length-1 do&lt;br /&gt;    begin&lt;br /&gt;      TRElement := DocTR.Item(i, 0) as IHtmlElement;&lt;br /&gt;      // находим все дочерние элементы (TD-элементы)&lt;br /&gt;      DocTD := TRElement.children as IHTMLElementCollection;&lt;br /&gt;&lt;br /&gt;      // прежде чем обращаться к первому и второму TD-элементу в коллекции,&lt;br /&gt;      // неплохо было бы проверить, есть ли там действительно 2 элемента (или больше)&lt;br /&gt;&lt;br /&gt;      if (DocTD.length &gt; 1) then&lt;br /&gt;        begin&lt;br /&gt;          TDElement := DocTD.Item(0, 0) as IHtmlElement;&lt;br /&gt;          // если в первом столбике написано Газпром, то это то, что мы ищем&lt;br /&gt;          if ( AnsiCompareStr( trim(TDElement.innerText), 'Газпром') = 0 ) then&lt;br /&gt;            begin&lt;br /&gt;              ShowMessage ( (DocTD.Item(1, 0) as IHtmlElement).innerText  );&lt;br /&gt;              exit;&lt;br /&gt;            end;&lt;br /&gt;        end;&lt;br /&gt;    end;&lt;br /&gt;&lt;br /&gt;  SLBody.Free;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Результат нажатия на кнопку :) представлен на скрине:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_dS8cdXcqCug/TBDQLXmcYPI/AAAAAAAAAQo/zg6Ak-DUpLY/s1600/micex_rbc_screen.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 209px;" src="http://4.bp.blogspot.com/_dS8cdXcqCug/TBDQLXmcYPI/AAAAAAAAAQo/zg6Ak-DUpLY/s400/micex_rbc_screen.png" border="0" alt="Парсинг с использованием IHTMLDocument2" title="Парсинг с использованием IHTMLDocument2" id="BLOGGER_PHOTO_ID_5481109640242290930" /&gt;&lt;/a&gt;&lt;br /&gt;Вот и все. Как это будет на CBuilder-е — не знаю.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;А сейчас об "акции", о которой я вскользь упомянула в начале поста.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Итак, если вы хотите, чтобы я разобрала работу инструмента/метода/и т.д. конкретно на вашем примере, вы можете написать мне письмо. Постараюсь один-два примера в неделю разбирать. Такие письма приходили и раньше, и в комментариях часто проскакивали вопросы, но — и вы должны меня понять, — большинство из них я пропускала мимо ушей из-за нехватки времени. "Что же изменилось? В чем подвох?" — спросите вы.&lt;br /&gt;&lt;br /&gt;Да, подвох есть. Вернее, мои условия.&lt;br /&gt;&lt;br /&gt;1. Ограничение под названием &lt;span style="font-weight:bold;"&gt;"не очень наглеть"&lt;/span&gt;. :) То есть ваша задача не должна отнять у меня более, чем полтора-два часа времени. Я просто физически не смогу пачками штамповать полноценные парсеры на все запросы. Предпочтения отдаются парсингу &lt;span style="font-weight:bold;"&gt;НЕ ПОИСКОВЫХ СИСТЕМ&lt;/span&gt;.&lt;br /&gt;Если все-таки вы заранее не оценили сложность и прислали &lt;span style="font-weight:bold;"&gt;объемную задачу&lt;/span&gt;, то в этом случае я, скорее всего, максимум разберу основные моменты и напишу &lt;span style="font-weight:bold;"&gt;часть&lt;/span&gt; кода.&lt;br /&gt;&lt;br /&gt;2. С вас же — "ссылка навсегда" (не в noindex) на пост, который я опубликую в ответ на ваш вопрос. Да, продаюсь за ссылки. Я в очередной раз решила уделять блогу больше времени, его развитию и продвижению. Буду рада, если таким образом вы мне поможете. Не считаю, что это дорого.&lt;br /&gt;&lt;br /&gt;Такие дела. Посмотрим, будут ли желающие :)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Чтобы участвовать в акции, достаточно написать мне письмо с вопросом (задачей), в котором указать язык написания (Delphi или PHP) и адрес блога, на котором планируете разместить ссылку.&lt;/span&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-5048388165526054053?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/bOZE3l4TkbsMz8-MQ30TH-RWg94/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/bOZE3l4TkbsMz8-MQ30TH-RWg94/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/bOZE3l4TkbsMz8-MQ30TH-RWg94/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/bOZE3l4TkbsMz8-MQ30TH-RWg94/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/d0SuLaiQsmY" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/06/delphi.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_dS8cdXcqCug/TBDQLXmcYPI/AAAAAAAAAQo/zg6Ak-DUpLY/s72-c/micex_rbc_screen.png" height="72" width="72" /><thr:total>13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-8051117782601564093</guid><pubDate>Sun, 06 Jun 2010 10:49:00 +0000</pubDate><atom:updated>2010-06-10T21:05:22.662+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">теория</category><category domain="http://www.blogger.com/atom/ns#">web-автоматизация</category><title>User-Agent и идентификация. Взгляд с разных сторон</title><description>Думаю, что все, кто занимается программированием для web, знают, что такое &lt;span style="font-weight:bold;"&gt;User-Agent&lt;/span&gt; и с чем его едят. Я решила немного обобщить знания и собрать их в одной статье. Сначала определение (&lt;a href="http://ru.wikipedia.org/wiki/User_Agent" rel="nofollow"&gt;Википедия в помощь&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;User Agent&lt;/span&gt; — это клиентское приложение, использующее определённый сетевой протокол. Термин обычно используется для приложений, осуществляющих доступ к веб-сайтам, таким как браузеры, поисковые роботы (и другие «пауки»), мобильные телефоны и другие устройства.&lt;br /&gt;При посещении веб-сайта клиентское приложение обычно посылает веб-серверу информацию о себе. Это текстовая строка, являющаяся частью HTTP запроса, начинающаяся с User-agent: или User-Agent:, и обычно включающая такую информацию, как название и версию приложения, операционную систему компьютера и язык. У «пауков» эта строка часто содержит URL и email-адрес, по которым веб-мастер может связаться с оператором «паука».&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;User-Agent и вебмастер&lt;/h2&gt;&lt;br /&gt;Что дает вебмастеру и оптимизатору знание о таком понятии как User-Agent? Если знать названия поисковых роботов (и роботов различных ресурсов, краулеров и т.д.), то можно самостоятельно регламентировать доступ этих роботов к различным частям сайта. Это делается в небезызвестном файле &lt;span style="font-weight:bold;"&gt;robots.txt&lt;/span&gt;, который должен находиться в корневой папке сайта.&lt;br /&gt;&lt;br /&gt;Вот как описан механизм работы робота Яндекса в зависимости от настроек в robots.txt (&lt;a href="http://help.yandex.ru/webmaster/?id=996567" rel="nofollow"&gt;полная версия мануала по использованию robots.txt&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;В роботе Яндекса используется сессионный принцип работы, на каждую сессию формируется определенный пул страниц, которые планирует закачать робот. Сессия начинается с закачки robots.txt сайта, если его нет, он не текстовый или на запрос робота возвращается HTTP-код отличный от '200', считается, что доступ роботу не ограничен. В самом robots.txt проверяется наличие записей, начинающихся с 'User-agent:', в них ищутся подстроки 'Yandex', либо '*' (регистр значения не имеет), причем, если обнаружено 'User-agent: Yandex', директивы для 'User-agent: *' не учитываются. Если записи 'User-agent: Yandex' и 'User-agent: *' отсутствуют, считается, что доступ роботу не ограничен.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Итак, для "управления" зоной видимости определенного робота надо добавить в robots.txt несколько строк. "Распознаются" директивы &lt;span style="font-weight:bold;"&gt;Allow&lt;/span&gt; и &lt;span style="font-weight:bold;"&gt;Disallow&lt;/span&gt;, соответственно разрешающие и запрещающие доступ роботу/краулеру/и т.д. к определенным частям вашего ресурса.&lt;br /&gt;&lt;br /&gt;Списки названий роботов поисковиков открыты, их можно без проблем найти в интернете. Вот, например, совсем недавно, буквально месяц назад (6 мая 2010 года), на блоге Яндеска был опубликован свеженький &lt;a href="http://webmaster.ya.ru/replies.xml?item_no=7783&amp;ncrnd=1247" rel="nofollow"&gt;список User-Agent-ов его роботов&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Итак, вы можете редактировать robots.txt, чтобы запретить или разрешить доступ определенных роботов к разным частям сайта. Рассмотрим конкретный случай: у Гугля есть новостной робот, но архивы вашего сайта не нуждаются в полном обходе этим роботом, поэтому пишем:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;User-agent: Googlebot-News&lt;br /&gt;Disallow: /archives&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если у вас чисто новостной сайт, вы можете захотеть запретить индексацию всего сайта всеми роботами Гугля кроме новостного. Тогда вам понадобится уже две группы директив:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;User-agent: Googlebot&lt;br /&gt;Disallow: /&lt;br /&gt;&lt;br /&gt;User-agent: Googlebot-News&lt;br /&gt;Disallow:&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Первая группа определит правило для всех гуглеботов, а вторая изменит установленные ранее правила конкретно для Googlebot-News.&lt;br /&gt;&lt;br /&gt;Помимо роботов поисковых систем по сети шарят множество всяких краулеров, собирающих контент для разных нужд. В буржунете их наблюдается больше, по крайней мере больше жалоб :) Краулеры могут поднимать нагрузки на ваш сервак на нежелательный уровень. А "палятся" некоторые из них, как вы уже, наверное, догадались, своими User-Agent-ами. В буржунете я не раз встречала целые списки юзерагентов для блокировки, выложенные в паблик. При желании вы можете их найти. Например: &lt;a href="http://johannburkard.de/blog/www/spam/The-top-10-spam-bot-user-agents-you-MUST-block-NOW.html" rel="nofollow"&gt;The top 10 spam bot user agents you MUST block. NOW.&lt;/a&gt; ("Десять User-agent-ов спам-ботов, которые вы ДОЛЖНЫ заблокировать. НЕМЕДЛЕННО.") Или, как вариант, можете посмотреть сайт &lt;a href="http://www.botsvsbrowsers.com/" rel="nofollow"&gt;Bots vs Browsers&lt;/a&gt;, на котором в момент написания статьи собрана информация о 499551 юзерагентах, из которых ботов - 4439. Или уже известный многим сайт www.user-agents.org.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; width: 400px; height: 26px;" src="http://2.bp.blogspot.com/_dS8cdXcqCug/TAu5CF2YjjI/AAAAAAAAAQg/Zgrq7jARBLk/s400/logo.png" border="0" alt="User-Agents vs browsers"id="BLOGGER_PHOTO_ID_5479676817207496242" /&gt;&lt;br /&gt;&lt;br /&gt;Информацию о юзерагентах ваших "посетителей" вы можете посмотреть в лог-файле статистики. Если увидите что-нибудь подозрительное — уже знаете, как поступить :)&lt;br /&gt;&lt;br /&gt;&lt;div class="info"&gt;Отмечу также, что для каждого движка есть отдельные наборы рекомендаций к правилам составления robots.txt для роботов поисковых систем. Прежде чем самостоятельно придумывать конфигурацию, поищите в Интернете.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;User-Agent и программист парсеров :)&lt;/h2&gt;&lt;br /&gt;Постоянные читатели, наверное, в недоумении: чего это я распространяюсь о том, как защититься от ботов? Для вновь прибывших повторюсь: я сама против воровства контента и его несанкционированного использования. Но целями парсинга может быть всякое (тут уж каждому по способностям).&lt;br /&gt;&lt;br /&gt;Итак, рассмотрю вопрос применения знаний о User-Agent-ах со стороны создателя парсеров. Все более-менее серьезные библиотеки работы с HTTP поддерживают возможность подстановки в заголовки формируемых запросов "левой" информации о браузере. Не забудьте поменять установленные по умолчанию значения на что-нибудь более правдоподобное. (Отсутствие информации о браузере подозрительно в первую очередь). У Indy, например, по умолчанию стоит&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Mozilla/3.0 (compatible; Indy Library)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ну как тут не спалиться? Это первый претендент на добавление в black-list.&lt;br /&gt;&lt;br /&gt;libwww, lwp-trivial, curl, PHP/, urllib, GT::WWW, Snoopy, MFC_Tear_Sample, HTTP::Lite, PHPCrawl, URI::Fetch, Zend_Http_Client, http client, PECL::HTTP — все это может вас выдать с головой.&lt;br /&gt;&lt;br /&gt;Самый простой способ, это создать файлик со всевозможными браузерами, а потом при организации запроса подставлять рандомно одну из записей. Ресурсов с базами подходящих User-Agent-ов - масса, ссылки на них я приводила в первой части статьи.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-8051117782601564093?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/08Mh9vQzEddD4Gw_bMWJnfkzC5I/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/08Mh9vQzEddD4Gw_bMWJnfkzC5I/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/08Mh9vQzEddD4Gw_bMWJnfkzC5I/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/08Mh9vQzEddD4Gw_bMWJnfkzC5I/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/MEemrurL_34" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/06/user-agent.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_dS8cdXcqCug/TAu5CF2YjjI/AAAAAAAAAQg/Zgrq7jARBLk/s72-c/logo.png" height="72" width="72" /><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-2506355526452731097</guid><pubDate>Mon, 31 May 2010 09:41:00 +0000</pubDate><atom:updated>2010-06-11T10:54:52.637+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">csv</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>PHP: построчное чтение и обработка больших CSV-файлов</title><description>С &lt;span style="font-weight:bold;"&gt;проблемой обработки больших CSV-файлов на PHP&lt;/span&gt; в первый раз я столкнулась недавно. На PHP я вообще мало программирую, только если возникают задачи написать что-либо конкретно на этом языке.&lt;br /&gt;&lt;br /&gt;В предыдущей статье были рассмотрены &lt;a href="http://parsing-and-i.blogspot.com/2010/05/csv-mysql.html"&gt;разные варианты импорта CSV-файла в базу данных MySQL&lt;/a&gt;. Там же я отметила, что работа с большими файлами требует особого подхода. Основным ограничением для импорта большого объема данных является время выполнения скрипта, которое задается хостером (как правило 30 секунд).&lt;br /&gt;&lt;br /&gt;Мне необходимо было именно автоматизировать процесс полного импорта. Перед вставкой в таблицу значения полей, полученные из scv-файла, требовали анализа и дополнительной обработки.&lt;br /&gt;&lt;br /&gt;Когда я прочитала в описании утилиты &lt;span style="font-weight:bold;"&gt;BigDump&lt;/span&gt; (в предыдущей статье я на нее ссылалась) о принципе работы:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The script executes only a small part of the huge dump and restarts itself. The next session starts where the last was stopped. (&lt;span style="font-weight:bold;"&gt;Перевод:&lt;/span&gt; Скрипт выполняет лишь небольшую часть SQL-команд из файла и перезапускает сам себя. В следующий раз импорт начинается с того места, в котором скрипт прервал свою работу.)&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;я поняла, что мне обязательно нужно попробовать такое решение. Поиски в инете чего-то похожего окончились успешно.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;$file_name = $_GET['path'];&lt;br /&gt;&lt;br /&gt;$conn = mysql_connect ("localhost", "username", "pass")&lt;br /&gt;     or die ("Соединение не установлено!");&lt;br /&gt;@mysql_select_db("db_name") or  die ("Соединение не установлено!");&lt;br /&gt;&lt;br /&gt;if (($handle_f = fopen($file_name, "r")) !== FALSE)&lt;br /&gt;{&lt;br /&gt;  // проверяется, надо ли продолжать импорт с определенного места&lt;br /&gt;  // если да, то указатель перемещается на это место&lt;br /&gt;  if(isset($_GET['ftell'])){&lt;br /&gt;      fseek($handle_f,$_GET['ftell']);&lt;br /&gt;  }&lt;br /&gt;  $i=0;&lt;br /&gt;  if(isset($_GET['x'])){&lt;br /&gt;      $x=$_GET['x'];&lt;br /&gt;  } else {&lt;br /&gt;      $x = 0;&lt;br /&gt;  }&lt;br /&gt;    &lt;br /&gt;  // построчное считывание и анализ строк из файла&lt;br /&gt;  while ( ($data_f = fgetcsv($handle_f, 1000, ";"))!== FALSE) {&lt;br /&gt;      $insert_q = 'insert into temp1 (code,contract,price,amount,dat_time,is_op) values '.&lt;br /&gt;      ' (\''.$data_f[0].'\',\''.$data_f[1].'\',\''.$data_f[2].'\',\''.$data_f[3].'\',\''.$data_f[4].'\',\'0\')';&lt;br /&gt;      @mysql_query($insert_q);&lt;br /&gt;&lt;br /&gt;      if(!strstr($i/5000,'.')){&lt;br /&gt;        print 'Importing record #: '.$x.'&amp;lt;br /&amp;gt;';&lt;br /&gt;        flush();&lt;br /&gt;        ob_flush();&lt;br /&gt;      }&lt;br /&gt;        &lt;br /&gt;      if($i==20000){&lt;br /&gt;          print '&amp;lt;meta http-equiv="Refresh" content="0; url='.$_SERVER['PHP_SELF'].'?x='.$x.'&amp;amp;amp;ftell='.ftell($handle_f).'&amp;amp;amp;path='.$_GET['path'].'"&amp;gt;';&lt;br /&gt;        exit;&lt;br /&gt;      }&lt;br /&gt;      $x++;&lt;br /&gt;      $i++;&lt;br /&gt;        &lt;br /&gt;  }&lt;br /&gt;    &lt;br /&gt;  fclose($handle_f);&lt;br /&gt;} else {$err = 1; echo "Не получилось открыть файл";}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;В параметре &lt;span style="font-weight:bold;"&gt;path&lt;/span&gt; при вызове скрипта передается путь к файлу, из которого надо производить импорт. В скрипте происходит импорт определенного количества строк (в примере - 20000), после чего он перезапускает сам себя с параметрами, среди которых кроме названия файла передается указатель на то место, с которого продолжать импорт (&lt;span style="font-weight:bold;"&gt;ftell&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Я протестировала этот скрипт на файле размером 60 Mb. Отработал он правильно, все проимпортировал. Но время работы, все-таки, хотелось бы уменьшить.&lt;br /&gt;&lt;br /&gt;В той же ветке форума, откуда я стырила это решение, обсуждалось, что ускорить работу скрипта при импорте данных в базу можно, заменив одиночные инсерты групповыми.&lt;br /&gt;&lt;div class="info"&gt;Команда INSERT, использующая VALUES, может быть использована для вставки сразу нескольких рядов. Чтобы сделать это, перечислите наборы значений, которые вам надо вставить. Пример:&lt;br /&gt;&lt;br /&gt;INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);&lt;/div&gt;&lt;br /&gt;Апгрейдив скрипт на групповую вставку, получила и вправду более подходящий по быстродействию результат. Но думаю, что на этом пока рано останавливаться, буду искать дальше.&lt;br /&gt;&lt;br /&gt;Несправедливо было бы обойти вниманием комментарий maxnag-а к предыдущему посту и не упомянуть о возможности &lt;a href="http://nagaychenko.com/blog/2010/04/29/%D0%BA%D0%B0%D0%BA-%D0%B8%D0%B7-csv-%D0%B7%D0%B0%D0%BD%D0%B5%D1%81%D1%82%D0%B8-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B2-mysql/" rel="nofollow"&gt;импорта данных из CSV средствами MySQL&lt;/a&gt;. Почитала &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/load-data.html" rel="nofollow"&gt;документацию по LOAD DATA INFILE&lt;/a&gt;, осталось потестировать на больших файлах :) Сначала я отмела для своего случая такой вариант, но потом решила, что, если он будет достаточно производительным, можно будет проимпортировать данные во временную таблицу, а затем произвести обработку и записать, куда надо. Но о результатах теста как-нибудь в следующий раз.&lt;br /&gt;&lt;br /&gt;Всем удачных решений! :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-2506355526452731097?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/vl9F1AQwJ4uIpA8NnT9JOyvwzF8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vl9F1AQwJ4uIpA8NnT9JOyvwzF8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/vl9F1AQwJ4uIpA8NnT9JOyvwzF8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vl9F1AQwJ4uIpA8NnT9JOyvwzF8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/qb_0X-6ahBk" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/05/php-csv.html</link><author>noreply@blogger.com (Masha)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-3666931776561364106</guid><pubDate>Thu, 27 May 2010 11:58:00 +0000</pubDate><atom:updated>2010-07-07T20:11:12.401+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">csv</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Запись данных из CSV-файлов в базу MySQL</title><description>Это вопрос из категории "для начинающих". Но тем не менее я решила его осветить на страницах блога, в угоду желанию объять необъятное и получить с поисковиков дополнительный трафик. :)&lt;br /&gt;&lt;br /&gt;Итак, у вас есть CSV-файл, и перед вами встала задача записать содержимое этого файла в базу.&lt;br /&gt;&lt;br /&gt;&lt;H2&gt;Импорт CSV-файла через PHPMyAdmin&lt;/H2&gt;&lt;br /&gt;Первый и самый простой способ — воспользоваться готовым инструментом. Например, функция импорта данных из csv-файла в базу есть в &lt;span style="font-weight:bold;"&gt;PHPMyAdmin&lt;/span&gt;-е.&lt;br /&gt;&lt;br /&gt;Выбираем нужную таблицу, на вкладке "Структура" внизу нажимаем на "&lt;span style="font-style:italic;"&gt;Вставить текстовые файлы в таблицу&lt;/span&gt;". Указаваем настройки импорта.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S_5fVPGyCLI/AAAAAAAAAQQ/cAIg5H4B36M/s1600/import_link.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 298px; height: 31px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S_5fVPGyCLI/AAAAAAAAAQQ/cAIg5H4B36M/s400/import_link.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5475919015365970098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S_5fVbo9H1I/AAAAAAAAAQY/GwvRvS4OKjo/s1600/import_options_phpmyadmin.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 277px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S_5fVbo9H1I/AAAAAAAAAQY/GwvRvS4OKjo/s400/import_options_phpmyadmin.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5475919018730528594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Внимание! Если после нажатия "Выполнить" у вас вылезли ошибки типа&lt;br /&gt;&lt;br /&gt;&lt;div class="error"&gt;ldi_check.php: Missing parameter: db&lt;br /&gt;ldi_check.php: Missing parameter: table&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;это может объясняться разными причинами.&lt;br /&gt;&lt;br /&gt;1. Возможно, &lt;span style="font-weight:bold;"&gt;файл, предназначенный для импорта, слишком большой&lt;/span&gt;. В старых версиях PHPMyAdmin (младше 2.7.0) был баг с импортом больших файлов. Так что, возможно, выходом из ситуации будет обновление PHPMyAdmin-а.&lt;br /&gt;&lt;br /&gt;&lt;div class="info"&gt;(Ссылка на &lt;a href="http://www.phpmyadmin.net/documentation/Documentation.html#faq1_16" rel="nofollow"&gt;пункт 1.16 FAQ&lt;/a&gt;) Начиная с версии 2.7.0 функция импорта была переписана и проблем с импортом больших файлов не должно возникать.&lt;br /&gt;&lt;br /&gt;Следующее, что можно проверить (или спросить у провайдера), - это значения параметров &lt;span style="font-weight:bold;"&gt;upload_max_filesize&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;memory_limit&lt;/span&gt; и &lt;span style="font-weight:bold;"&gt;post_max_size&lt;/span&gt; в конфигурационном файле &lt;span style="font-weight:bold;"&gt;php.ini&lt;/span&gt;. Эти три настройки ограничивают максимальный размер данных, которые могут быть переданы и обработаны PHP.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Там же, в документации, приводится описание нескольких "обходных путей", которые подойдут, если вы импортируете не scv-файл, а заливаете дамп базы, а ваш провайдер не хочет менять настройки:&lt;br /&gt;&lt;br /&gt;а) Проверьте настройку &lt;a href="http://www.phpmyadmin.net/documentation/Documentation.html#cfg_UploadDir" rel="nofollow"&gt;$cfg['UploadDir']&lt;/a&gt;. Она позволяет настроить загрузку файла на сервер через scp, ftp или другим методом. PhpMyAdmin может работать с файлами, расположенными во временном каталоге. Более подробную информацию читайте в разделе &lt;a href="http://www.phpmyadmin.net/documentation/Documentation.html#config" rel="nofollow"&gt;Настройки&lt;/a&gt; документации.&lt;br /&gt;&lt;br /&gt;б) Используйте сторонние утилиты (например, &lt;a href="http://www.ozerov.de/bigdump.php" rel="nofollow"&gt;BigDump&lt;/a&gt;) для того, чтобы разбить файл перед загрузкой.&lt;br /&gt;&lt;br /&gt;в) Если у вас есть прямой shell доступ, используйте MySQL для импорта файлов напрямую. Вы можете это сделать с помощью команды "source":&lt;br /&gt;&lt;pre&gt;source filename.sql&lt;/pre&gt;&lt;br /&gt;2. Если файл небольшой, но эти ошибки все же появляются, то вам может помочь вот эта &lt;a href="http://www.webmasterworld.com/forum88/8393.htm" rel="nofollow"&gt;тема на форуме&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;H2&gt;Парсинг CSV-файла с помощью PHP&lt;/H2&gt;&lt;br /&gt;Далее — программные способы. Они подойдут вам, если вы хотите все это дело автоматизировать или если перед непосредственной вставкой вам нужно провести дополнительную обработку данных.&lt;br /&gt;&lt;br /&gt;&lt;div class="warning"&gt;Если вы предполагаете работать с большими файлами, то так просто описанные ниже способы вам тоже могут не подойти. Вам нужно будет провести на подготовительном этапе действия, описанные в пункте об импорте больших файлов через PHPMyAdmin.&lt;/div&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Использование fgetcsv&lt;/h3&gt;&lt;br /&gt;&lt;pre&gt;array fgetcsv (resource descriptor, int lenght [, string separator [, string offset]])&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Функция построчно считывает данные, анализирует и возвращает поля в виде массива. Параметрами функции служат дескриптор файла, максимальная длина строки, разделитель полей и смещение. В случае ошибки или при достижении конца файла возвращается FALSE.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$columns = "`code`,`contract`,`price`,`amount`,`dat_time`,`trade_id`,`Nosystem`";&lt;br /&gt;if ( ($handle_o = fopen($file_name, "r") ) !== FALSE ) {&lt;br /&gt;    while ( ($data_o = fgetcsv($handle_o, 1000, ";")) !== FALSE) {&lt;br /&gt;      $insertValues = array();&lt;br /&gt;      foreach( $data_o as $v ) {&lt;br /&gt;         $insertValues[]="'".addslashes(trim($v))."'";&lt;br /&gt;      }&lt;br /&gt;      $values=implode(',',$insertValues);&lt;br /&gt;      $sql = "INSERT INTO `sdelka_temp` ( $columns ) VALUES ( $values )";&lt;br /&gt;      mysql_query($sql) or die('SQL ERROR:'.mysql_error());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;fclose($handle_o);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если названия столбцов прописаны в первой строке CSV-файла (обычно именно так и бывает), то красивее будет написать так:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if ( ($handle_o = fopen($file_name, "r") ) !== FALSE ) {&lt;br /&gt;    // читаем первую строку и разбираем названия полей&lt;br /&gt;    $columns_o = fgetcsv($handle_o, 1000, ";");&lt;br /&gt;    foreach( $columns_o as $v ) {&lt;br /&gt;       $insertColumns[]="'".addslashes(trim($v))."'";&lt;br /&gt;    }&lt;br /&gt;    $columns=implode(',',$insertColumns);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    while ( ($data_o = fgetcsv($handle_o, 1000, ";")) !== FALSE) {&lt;br /&gt;      $insertValues = array();&lt;br /&gt;      foreach( $data_o as $v ) {&lt;br /&gt;         $insertValues[]="'".addslashes(trim($v))."'";&lt;br /&gt;      }&lt;br /&gt;      $values=implode(',',$insertValues);&lt;br /&gt;      $sql = "INSERT INTO `sdelka_temp` ( $columns ) VALUES ( $values )";&lt;br /&gt;      mysql_query($sql) or die('SQL ERROR:'.mysql_error());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;fclose($handle_o);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если данные перед вставкой нуждаются в дополнительной обработке, то без проблем можно производить обработку массива значений, возвращаемого функцией fgetcsv, обращаясь к элементам по индексу.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Библиотеки для импорта CSV&lt;/h3&gt;&lt;br /&gt;Если поискать в интернете, можно найти не одну библиотеку для импорта CSV. Они не очень отличаются по быстродействию, но раз уж специально разрабатывались для этих целей, то разработчики видели смысл в своей работе. Мне кажется, что под каждый конкретный случай у какой-то из библиотек может быть преимущество. Все зависит от данных в файле.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;a href="http://www.legend.ws/blog/tips-tricks/csv-php-mysql-import/"&gt;simplecsvimport&lt;/a&gt; (&lt;a href="http://www.legend.ws/blog/simplecsvimport.zip"&gt;Скачать&lt;/a&gt;.)&lt;/span&gt;&lt;br /&gt;Библиотека позволяет импортировать данные в базу.&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Принцип:&lt;/span&gt; считывание данных построчно, разбор отдельно каждой строки (значения полей "подчищаются"; где надо символы экранируются), формирование sql-запроса. Предусмотрена возможность сохранения полученных insert-ов в виде дампа. Возможно настроить добавление первого пустого поля (нужно в случае, если в базе поле автоинкрементируется).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;a href="http://solomongaby.users.phpclasses.org/package/2917-PHP-Import-CSV-data-into-a-MySQL-database-table.html"&gt;Quick CSV import&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;Эту библиотеку я не тестировала, но если кто протестирует — поделитесь опытом в комментариях, пожалуйста. Не зря же Quick вынесено в ее название :)&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-3666931776561364106?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/rgJyffYN3NZhoDxpFMcR-qy0B6o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rgJyffYN3NZhoDxpFMcR-qy0B6o/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/rgJyffYN3NZhoDxpFMcR-qy0B6o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rgJyffYN3NZhoDxpFMcR-qy0B6o/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/MMKKW7pRrFg" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/05/csv-mysql.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S_5fVPGyCLI/AAAAAAAAAQQ/cAIg5H4B36M/s72-c/import_link.gif" height="72" width="72" /><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-5318457579382402695</guid><pubDate>Thu, 20 May 2010 13:00:00 +0000</pubDate><atom:updated>2010-06-11T10:55:42.271+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">Simple HTML DOM</category><title>PHP Simple HTML DOM Parser</title><description>Сегодня я немного расскажу про библиотеку для парсинга HTML под названием &lt;span style="font-weight:bold;"&gt;PHP Simple HTML DOM Parser&lt;/span&gt;. В последнее время частенько ей пользовалась: нравятся ее возможности и простота. Скачать библиотеку можно со &lt;a href="http://sourceforge.net/projects/simplehtmldom/files/" rel="nofollow"&gt;страницы&lt;/a&gt;. В комментарии к сказано:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;A simple PHP HTML DOM parser written in PHP5+, supports invalid HTML, and provides a very easy way to handle HTML elements.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;То есть нам обещают массу плюсов, основные из которых скорость и поддержка невалидного html-кода. Изучив &lt;a href="http://simplehtmldom.sourceforge.net/manual.htm" rel="nofollow"&gt;документацию&lt;/a&gt;, можно вдохновиться на подвиги: возможности библиотеки действительно впечатляют. Кроме всего прочего - удобный, интуитивно понятный синтаксис.&lt;br /&gt;&lt;br /&gt;Еще в числе плюсов, которые я отметила, — отсутствие проблем с кодировками. Часто бывает, что, получив содержимое страницы с помощью, например, file_get_contents, кодировку данных на промежуточном этапе приходится преобразовывать. Здесь же такой надобности у меня пока что не возникало.&lt;br /&gt;&lt;br /&gt;С помощью этой библиотеки вы можете обращаться к элементам и атрибутам элементов, искать определенного уровня вложенные элементы, фильтровать их, искать текст и комментарии(!).&lt;br /&gt;&lt;br /&gt;Приведу примеры из документации:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Найти ссылки и возвратить массив найденных объектов&lt;br /&gt;$ret = $html-&gt;find('a');&lt;br /&gt;&lt;br /&gt;// Найти (N)-ую по счету ссылку и возвратить найденный объект или null в случае, если объект не найден&lt;br /&gt;$ret = $html-&gt;find('a', 0);&lt;br /&gt;&lt;br /&gt;// Найти все элементы &amp;lt;div&amp;gt;, у которых id=foo&lt;br /&gt;$ret = $html-&gt;find('div[id=foo]'); &lt;br /&gt;&lt;br /&gt;// Найти все элементы &amp;lt;div&amp;gt;, имеющие атрибут id&lt;br /&gt;$ret = $html-&gt;find('div[id]');&lt;br /&gt;&lt;br /&gt;// Найти все элементы, имеющие атрибут id&lt;br /&gt;$ret = $html-&gt;find('[id]');&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ну и, конечно, стандарто - в библиотеку заложена возможность перемещения по списку элементов объектного дерева. Для этого используются:&lt;br /&gt;&lt;pre&gt;$e-&gt;children( [int $index] ), &lt;br /&gt;$e-&gt;parent(), &lt;br /&gt;$e-&gt;first_child(), &lt;br /&gt;$e-&gt;last_child(),&lt;br /&gt;$e-&gt;next_sibling(), &lt;br /&gt;$e-&gt;prev_sibling().&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Прежде чем на страницах этого блога приводить примеры парсеров, написанных с использованием этой библиотеки, я решила протестировать, насколько соответствует реальности обещанное быстродействие. Явных тормозов я за ней не замечала, но хотелось бы получить доказательство в цифрах.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Для теста возьмем элементарную задачку - выведем на экран ссылки с анкорами с главной страницы Гугля. Для сравнения буду использовать работу через стандартный &lt;a href="http://parsing-and-i.blogspot.com/2009/08/html-php.html"&gt;domDocument&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Вот так будет выглядеть код с использованием &lt;span style="font-weight:bold;"&gt;Simple HTML DOM Parser&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;include_once('simple_html_dom.php');&lt;br /&gt;&lt;br /&gt;// Создаем объект DOM на основе кода, полученного по ссылке&lt;br /&gt;$html = file_get_html('http://www.google.com/');&lt;br /&gt;&lt;br /&gt;// находим все ссылки&lt;br /&gt;foreach($html-&gt;find('a') as $element) &lt;br /&gt;    echo $element-&gt;href .' ('. $element-&gt;innertext. ')&amp;lt;br&amp;gt;';&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;А вот код с использованием &lt;span style="font-weight:bold;"&gt;domDocument&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$html = file_get_contents('http://www.google.com/');&lt;br /&gt;// создаем новый dom-объект&lt;br /&gt;$dom = new domDocument;&lt;br /&gt;&lt;br /&gt;// загружаем html в объект&lt;br /&gt;$dom-&gt;loadHTML($html);&lt;br /&gt;$dom-&gt;preserveWhiteSpace = false;&lt;br /&gt;&lt;br /&gt;// элемент по тэгу&lt;br /&gt;foreach ($dom-&gt;getElementsByTagName('a') as $row)&lt;br /&gt;    echo $row-&gt;GetAttribute('href').' ('.$row-&gt;nodeValue.')&amp;lt;br&amp;gt;';&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Проведя по 5 запусков каждого варианта получила среднее время выполнения скрипта:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;для Simple HTML DOM - 0,8096&lt;/li&gt;&lt;br /&gt;&lt;li&gt;для domDocument - 0,7326&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Тут, хотя и Simple HTML DOM проигрывает, результаты различаются не сильно. Думаю, что надо будет потом протестировать скорость на чем-нибудь более сложном, с поиском и перемещением по узлам DOM-дерева.&lt;br /&gt;&lt;br /&gt;Зато я визуально смогла оценить работу библиотеки Simple HTML DOM Parser с "невалидным" html. Если перед строкой &lt;br /&gt;&lt;blockquote&gt;$dom-&gt;loadHTML($html);&lt;/blockquote&gt;&lt;br /&gt;не поставить "@", то при работе скрипта с domDocument вывалится куча варнингов типа:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Warning: DOMDocument::loadHTML() [function.DOMDocument-loadHTML]: Tag nobr invalid in Entity, line: 5&lt;br /&gt;Warning: DOMDocument::loadHTML() [function.DOMDocument-loadHTML]: htmlParseEntityRef: expecting ';' in Entity, line: 5&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Кроме того видна разница и в выводе результатов (кодировка):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_dS8cdXcqCug/S_UzhBztpyI/AAAAAAAAAP4/Qt0MNL0UFIk/s1600/res_simple_dom.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 180px;" src="http://1.bp.blogspot.com/_dS8cdXcqCug/S_UzhBztpyI/AAAAAAAAAP4/Qt0MNL0UFIk/s400/res_simple_dom.png" border="0" alt="Simple HTML DOM Parser library"id="BLOGGER_PHOTO_ID_5473337564652742434" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S_Uzgz5Eq0I/AAAAAAAAAPw/dK6OAmueT3s/s1600/res_domdocument.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 180px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S_Uzgz5Eq0I/AAAAAAAAAPw/dK6OAmueT3s/s400/res_domdocument.png" border="0" alt="domDocument"id="BLOGGER_PHOTO_ID_5473337560917125954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В общем, библиотека мне нравится, кто еще не пользовался — советую попробовать.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-5318457579382402695?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/stgwNvJ26nbVztGjca6SVzJDqto/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/stgwNvJ26nbVztGjca6SVzJDqto/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/stgwNvJ26nbVztGjca6SVzJDqto/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/stgwNvJ26nbVztGjca6SVzJDqto/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/w90maxGF-C0" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/05/php-simple-html-dom-parser.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_dS8cdXcqCug/S_UzhBztpyI/AAAAAAAAAP4/Qt0MNL0UFIk/s72-c/res_simple_dom.png" height="72" width="72" /><thr:total>23</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-4706686380512793736</guid><pubDate>Tue, 27 Apr 2010 09:10:00 +0000</pubDate><atom:updated>2010-06-11T10:56:02.647+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">IHTMLDocument2</category><title>Работа с комбобоксами (SELECT) в TWebbrowser</title><description>В интернете достаточно материала о том, &lt;span style="font-weight:bold;"&gt;как работать с элементом SELECT в TWebBrowser&lt;/span&gt;. У себя на блоге я мельком уже задевала уже этот вопрос, но вот решила написать отдельный пост.&lt;br /&gt;Тем, для кого этот материал вновинку, сначала советую ознакомиться со статьями:&lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://parsing-and-i.blogspot.com/2009/06/twebbrowser-oleobject.html"&gt;TWebBrowser, OleObject и его свойства&lt;/a&gt;.&lt;br /&gt;2. &lt;a href="http://parsing-and-i.blogspot.com/2009/09/twebbrowser.html"&gt;Работа с формами в TWebBrowser&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Обычно я делаю так: сначала ищу на форме нужный селект по имени (тип искомого объекта - IHTMLSelectElement (не забудьте подключить библиотеку &lt;span style="font-weight:bold;"&gt;MSHTML&lt;/span&gt;)). Селект можно находить &lt;span style="font-weight:bold;"&gt;по id или по name&lt;/span&gt;, в зависимости от требований. Вот пример функции, возращающей требуемый элемент (проверяется и по id, и по name):&lt;br /&gt; &lt;br /&gt;&lt;pre&gt;function FindCBByName(CBName: String) : IHTMLSelectElement;&lt;br /&gt;&lt;br /&gt;function TMainF.FindCBByName(CBName: String): IHTMLSelectElement;&lt;br /&gt;var&lt;br /&gt;  i : integer;&lt;br /&gt;  DocSelect   : IHTMLElementCollection;&lt;br /&gt;  DocElement  : IHtmlElement;&lt;br /&gt;  Doc         : IHTMLDocument2;&lt;br /&gt;begin&lt;br /&gt;  Result := nil;&lt;br /&gt;  WB.Document.QueryInterface(IHTMLDocument2, Doc);&lt;br /&gt;  DocSelect := Doc.all.tags('SELECT') as IHTMLElementCollection;&lt;br /&gt;&lt;br /&gt;  for i := 0 to DocSelect.length-1 do&lt;br /&gt;    begin&lt;br /&gt;      DocElement := DocSelect.Item(i, 0) as IHtmlElement;&lt;br /&gt;      if (UpperCase(DocElement.id) = UpperCase( CBName )) or  &lt;br /&gt;         (UpperCase(DocElement.getAttribute('name',0)) = UpperCase( CBName )) then&lt;br /&gt;        begin&lt;br /&gt;          Result := DocElement as IHTMLSelectElement;&lt;br /&gt;          exit;&lt;br /&gt;        end;&lt;br /&gt;    end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;В случае, если объект не найден, возвращается nil. В данном примере у меня привязка к объекту TWebBrowser (WB) на форме. Можно избавиться от нее, добавив TWebBrowser в качестве параметра функции (я так обычно и делаю).&lt;br /&gt;&lt;br /&gt;Когда элемент найден, в нем надо будет выбрать значение. В зависимости от задачи, требуется обычно выбрать option по тексту или по value.&lt;br /&gt;&lt;br /&gt;В качестве примера привожу процедурку, которая уже "светилась" в статьях на этом блоге.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;procedure SelectItemByTextFromSelectEl (TextStr : string;&lt;br /&gt;          SelectEl : IHTMLSelectElement; var vSelected : boolean); // выбор оптионза по тексту из селекта&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Код:&lt;br /&gt;&lt;pre&gt;procedure TEmailF.SelectItemByTextFromSelectEl(TextStr: string;&lt;br /&gt;  SelectEl: IHTMLSelectElement; var vSelected : boolean);&lt;br /&gt;var&lt;br /&gt;  i, t   : integer;&lt;br /&gt;  iDisp : IDispatch;&lt;br /&gt;  iColl : IHTMLElementCollection;&lt;br /&gt;  iOption : IHTMLOptionElement;&lt;br /&gt;begin&lt;br /&gt;  iDisp := SelectEl.tags('OPTION');&lt;br /&gt;  iDisp.QueryInterface(IHTMLElementCollection, iColl);&lt;br /&gt;  if not Assigned(iColl) then&lt;br /&gt;    begin&lt;br /&gt;      // sLog('log.txt','Not assigned!');&lt;br /&gt;      exit;&lt;br /&gt;    end;&lt;br /&gt;&lt;br /&gt;  i := 0;&lt;br /&gt;  while i &lt;= iColl.length-1 do&lt;br /&gt;    begin&lt;br /&gt;      iDisp := iColl.item(i,0);&lt;br /&gt;      iDisp.QueryInterface(IHTMLOptionElement, iOption);&lt;br /&gt;      if Assigned(iOption) then&lt;br /&gt;        begin&lt;br /&gt;          if iOption.text = TextStr then&lt;br /&gt;            begin&lt;br /&gt;              iOption.selected := true;&lt;br /&gt;              vSelected := true;&lt;br /&gt;              break;&lt;br /&gt;            end;&lt;br /&gt;        end;&lt;br /&gt;      inc(i);&lt;br /&gt;    end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Переменная vSelected становится равной true, если выбор сделан успешно. Если нужен выбор по value, процедура будет аналогичной, изменится лишь одна строка&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if iOption.text = TextStr then&lt;/pre&gt;&lt;br /&gt;на&lt;br /&gt;&lt;pre&gt;if iOption.value = ValueStr then&lt;/pre&gt;&lt;br /&gt;Можно сделать и универсальную процедуру, в которую в качестве параметра передавать, по чему искать - по тексту или по value. Обычно я использую еще более апгрейженную процедуру, в которую передаю, например, какой джаваскрипт выполнить после осуществления выбора.&lt;br /&gt;&lt;br /&gt;Закрепляем материал. Пример применения:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S9arONqKN-I/AAAAAAAAAPo/6tnKktov9WI/s1600/yandex_secret_quest.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 91px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S9arONqKN-I/AAAAAAAAAPo/6tnKktov9WI/s400/yandex_secret_quest.png" border="0" alt="option select twebbrowser" title="Delphi - выбор OPTION в SELECT-е в TWebBrowser" id="BLOGGER_PHOTO_ID_5464743458533685218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;var&lt;br /&gt;  SelectQuest   : IHTMLSelectElement;&lt;br /&gt;  vQSelected, vQSelected2 : boolean;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;SelectQuest := FindCBByName('hintq');&lt;br /&gt;if (Assigned(SelectQuest)) then&lt;br /&gt;  begin&lt;br /&gt;    vQSelected := false;&lt;br /&gt;    // пытаемся выбрать вопрос по умолчанию&lt;br /&gt;    SelectItemByTextFromSelectEl( teQUEST.Text, SelectQuest, vQSelected);&lt;br /&gt;    // если вопрос удачно выбран, то вводим ответ&lt;br /&gt;    if (vQSelected) then&lt;br /&gt;      begin&lt;br /&gt;        //  ShowMessage('Вопрос был в стандартных');&lt;br /&gt;        // разные действия&lt;br /&gt;      end&lt;br /&gt;    else  // если вопроса нет в стандартных, выбираем свой вопрос&lt;br /&gt;      begin&lt;br /&gt;        //  ShowMessage('Вопроса не было в стандартных');&lt;br /&gt;        vQSelected2 := false;&lt;br /&gt;        SelectItemByValueFromSelectEl('99', SelectQuest, vQSelected2);&lt;br /&gt;        // разные действия&lt;br /&gt;      end;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-4706686380512793736?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sqSYt934M4cKPPl1IfbZxCfads0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sqSYt934M4cKPPl1IfbZxCfads0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sqSYt934M4cKPPl1IfbZxCfads0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sqSYt934M4cKPPl1IfbZxCfads0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/jfCgKnOLDt0" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/04/select-twebbrowser.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S9arONqKN-I/AAAAAAAAAPo/6tnKktov9WI/s72-c/yandex_secret_quest.png" height="72" width="72" /><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-3706065276101260844</guid><pubDate>Fri, 23 Apr 2010 10:31:00 +0000</pubDate><atom:updated>2010-06-11T10:56:37.632+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">linux</category><title>Установка web-сервера (XAMPP) на Ubuntu на Eee PC</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S9F4dm2V_AI/AAAAAAAAAPY/mpEC0e1vA_E/s1600/asus-eee-pc-xampp.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 192px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S9F4dm2V_AI/AAAAAAAAAPY/mpEC0e1vA_E/s320/asus-eee-pc-xampp.jpg" border="0" alt="install xampp on eee pc" title="Установка XAMPP на Eee PC" id="BLOGGER_PHOTO_ID_5463280273017142274" /&gt;&lt;/a&gt;&lt;br /&gt;Чтобы в дороге и в отпуске заниматься программированием, решила &lt;span style="font-weight:bold;"&gt;на нетбук установить сервер&lt;/span&gt;. Порывшись в инете, остановилась на &lt;a href="http://www.apachefriends.org/en/xampp-linux.html" rel="nofollow"&gt;XAMPP&lt;/a&gt;. Скачала дистрибутив последней версии с официального сайта. При попытке установить пакет на Eee PC, вылезло сообщение о том, что на диске недостаточно места. Меня это удивило, так как места было вполне достаточно. Но, как потом оказалось, не там, где надо :) С линуксом я до этого момента практически не имела дела, поэтому на разбирательства ушло какое-то время.&lt;br /&gt;&lt;br /&gt;Сначала пришлось познакомиться с некоторыми базовыми командами.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Консоль&lt;/span&gt; можно вызвать с помощью сочетания горячих клавиш &lt;span style="font-weight:bold;"&gt;"Ctrl+Alt+T"&lt;/span&gt;. Если оно не срабатывает (было, что один раз сглючило, и положение исправилось только после перезагрузки), то можно загрузить &lt;span style="font-weight:bold;"&gt;диспетчер файлов&lt;/span&gt; и при открытом окне нажать &lt;span style="font-weight:bold;"&gt;"Ctrl+T"&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cd - смена текущей директории&lt;br /&gt;ls - листинг каталога&lt;br /&gt;chmod - изменение прав доступа на файл/директорию&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;и так далее. В принципе, тех, которые мне потребовались, с полдесятка.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Вернемся к проблеме. Дело заключалось в том, что раздел, отведенный для установки программ, был забит под завязку (вообще нетбуки по задумке производителей предназначены только для выхода в интернет), а установочный пакет был заточен именно под установку в конкретную директорю (а именно - в opt в корне).&lt;br /&gt;&lt;br /&gt;Посмотреть свободное дисковое пространство (а заодним и структуру разделов) можно командой &lt;span style="font-weight:bold;"&gt;df&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/&amp;gt; df&lt;br /&gt;Файловая система     1K-блоков      Исп  Доступно  Исп% смонтирована на&lt;br /&gt;rootfs                  695716    695640         0 100% /&lt;br /&gt;/dev/sda1              3161695   2801119    360576  89% /.ro&lt;br /&gt;/dev/sda2               695716    695640         0 100% /.rw&lt;br /&gt;none                    695716    695640         0 100% /&lt;br /&gt;tmpfs                   514084         8    514076   1% /dev/shm&lt;br /&gt;tmpfs                   514084         0    514084   0% /media&lt;br /&gt;tmpfs                   131072        12    131060   1% /tmp&lt;br /&gt;/dev/sdb1             15512328  12512820   2211524  85% /home&lt;br /&gt;/dev/sda1              3161695   2801119    360576  89% /ro&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;А еще, чтобы просто посмотреть структуру, можно использовать mount без параметров:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/&amp;gt; mount&lt;br /&gt;rootfs on / type rootfs (rw)&lt;br /&gt;/dev/sda1 on /.ro type ext2 (ro)&lt;br /&gt;/dev/sda2 on /.rw type ext3 (rw,noatime,data=ordered)&lt;br /&gt;none on / type aufs (rw,noxino,br:/.rw=rw:/.ro=ro)&lt;br /&gt;proc on /proc type proc (rw)&lt;br /&gt;sysfs on /sys type sysfs (rw)&lt;br /&gt;devpts on /dev/pts type devpts (rw)&lt;br /&gt;tmpfs on /dev/shm type tmpfs (rw)&lt;br /&gt;tmpfs on /media type tmpfs (rw)&lt;br /&gt;tmpfs on /tmp type tmpfs (rw)&lt;br /&gt;/dev/sdb1 on /home type ext3 (rw,noatime,data=ordered)&lt;br /&gt;usbfs on /proc/bus/usb type usbfs (rw)&lt;br /&gt;/dev/sda1 on /ro type ext2 (ro)&lt;br /&gt;/dev/sdc on /media/D: type vfat (rw,nosuid,nodev,noexec,fmask=0111,dmask=0000,codepage=cp850,iocharset=utf8,shortname=mixed)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Я пробовала установить дистрибутив в папку в другом разделе, но при попытке запуска сервера вылезали предупреждения, что приложение не может бла-бла-бла чего-то найти по нужному пути.&lt;br /&gt;И тут на меня нашло озарение спросить у нашего админа, что мне делать.&lt;br /&gt;&lt;br /&gt;Он сказал, что на моем месте "&lt;span style="font-style:italic;"&gt;папку /opt перенёс бы в /home, а потом сделал симлинк: /opt -&gt; /home/opt&lt;/span&gt;":&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo mv /opt /home &amp;&amp; ln -s /home/opt /opt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Команду следует выполнить под рутом. "&amp;&amp;" выполняет команду справа, если команда слева успешно завершилась (с кодом 0). Сделала так, потом установила приложение - и все заработало.&lt;br /&gt;&lt;br /&gt;Запускается сервер командой:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo /home/opt/xampp/xampp start&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;После этого можно спокойно себе скриптить и тестировать :)&lt;br /&gt;&lt;br /&gt;Завершается работа сервера командой:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo /home/opt/xampp/xampp stop&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;И все-таки Linux для меня пока что в новинку, часто приходится лезть в интернет, чтобы посмотреть ответы на всякие по мелочам возникающие вопросы. А они возникают, уж поверьте! Может, еще вернусь к этой теме на страницах блога. Главное, что сейчас я могу программировать под web где угодно :)&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"  rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-3706065276101260844?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/XFDC2oVS8K8HG1NAlLENUuWmbPM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XFDC2oVS8K8HG1NAlLENUuWmbPM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/XFDC2oVS8K8HG1NAlLENUuWmbPM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XFDC2oVS8K8HG1NAlLENUuWmbPM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/F39UPFg82mg" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/04/web-xampp-ubuntu-eee-pc.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S9F4dm2V_AI/AAAAAAAAAPY/mpEC0e1vA_E/s72-c/asus-eee-pc-xampp.jpg" height="72" width="72" /><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-7239158047032624521</guid><pubDate>Thu, 01 Apr 2010 10:06:00 +0000</pubDate><atom:updated>2010-04-21T13:16:54.984+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">RSSAdder</category><title>RSSAdder. Свежий конфиг</title><description>Сегодня возникла необходимость прогнать один фид по RSS-агрегаторам. &lt;a href="http://parsing-and-i.blogspot.com/2009/08/rssadder-rss-rss.html"&gt;RSSAdder&lt;/a&gt; я все еще не до конца переделала, поэтому прогоняла старой версией. Попутно решила обновить данные в конфиге. Результаты обновления были, мягко говоря, "не очень": из 65 каталогов в списке осталось только 51. Остальные так или иначе покинули всемирную паутину или переродились во что-нибудь еще более говнистое :) Потом я вспомнила о конфигах, давным-давно присланных одним из читателей блога, добавила в общий список еще 5 каталогов. Получилось 56 проверенных ресурсов.&lt;br /&gt;&lt;br /&gt;Выкладываю &lt;a href="http://narod.ru/disk/19299211000/RSSAdder.ini.html"&gt;свежий список&lt;/a&gt; и еще &lt;a href="http://narod.ru/disk/19299049000/RSSAdder-pinger.ini.html"&gt;конфиг со списком пинг-сервисов&lt;/a&gt;, так же предоставленный этим благородным читателем %D. Сразу скажу, что пинг-сервисы не проверены, если заметите ошибки — пишите. Хотя, судя по тому, что в старом списке со временем появилось столько битых каталогов, но никто об этом не написал, обратной связи особо ждать не следует :) В общем, еще раз гранд мерси читателю, подписавшемуся как LookUp, все остальным — свежие конфиги. (Для обновления просто скопируйте файл в папку поверх старого).&lt;br /&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons" rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-7239158047032624521?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/COZZWw7fC1gB4ABqw2stZEHEovE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/COZZWw7fC1gB4ABqw2stZEHEovE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/COZZWw7fC1gB4ABqw2stZEHEovE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/COZZWw7fC1gB4ABqw2stZEHEovE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/caieQVs04vo" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/04/rssadder.html</link><author>noreply@blogger.com (Masha)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-1240835752070806510</guid><pubDate>Tue, 23 Mar 2010 11:59:00 +0000</pubDate><atom:updated>2010-03-23T15:28:21.831+03:00</atom:updated><title>Вопросы-ответы</title><description>Вдогонку к предыдущему посту (и отвечая на вопрос, заданный по почте):&lt;br /&gt;&lt;br /&gt;Чтобы просмотреть в Google Chrome код страницы (например, xml, сгенерированного с помощью php), нужно написать в адресной строке префикс &lt;span style="font-weight:bold;"&gt;"view-source:"&lt;/span&gt;. Например:&lt;br /&gt;&lt;span style="font-style:italic;"&gt;view-source:http://site.ru/xml.php?id=262&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Пользуясь случаем, скажу вот еще что: уважаемые читатели блога, я получаю от вас на почту много писем с вопросами. Нет, если вам, конечно, хочется задать вопрос — задавайте. Но неплохо было бы перед этим хотя бы поискать ответ в интернете. Вопросы порой бывают очень банальными, а времени у меня не безмерная куча, и тратить его хочется все-таки с какой-никакой, но пользой.&lt;br /&gt;&lt;br /&gt;Могу порекомендовать сервис вопросов-ответов &lt;a href="http://help.sander.su/"&gt;help.sander.su&lt;/a&gt;, о котором уже недавно упоминала в комментариях к одному из сообщений. Я буду перенаправлять туда вопросы, с решением которых сама не сталкивалась и на разбор которых нет времени. И, соответственно, отвечать там на те, с которыми сталкивалась :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-1240835752070806510?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/tekINgW7YaV8cy_BcJ3IZWjgBAM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/tekINgW7YaV8cy_BcJ3IZWjgBAM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/tekINgW7YaV8cy_BcJ3IZWjgBAM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/tekINgW7YaV8cy_BcJ3IZWjgBAM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/8UCyTv4yu0w" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/03/blog-post.html</link><author>noreply@blogger.com (Masha)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-106708389425436615</guid><pubDate>Tue, 23 Mar 2010 11:04:00 +0000</pubDate><atom:updated>2010-06-11T10:57:19.620+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">узкие места</category><title>Польза от исследования кода в разных браузерах</title><description>Поделюсь наблюдением: &lt;span style="font-style:italic;"&gt;не следует забывать про разные браузеры на этапе исследования кода страницы&lt;/span&gt;. Иногда может случиться так, что обход косяков &lt;span style="font-weight:bold;"&gt;IE&lt;/span&gt;, который вебмастеру приходится отдельно предусматривать, может натолкнуть на решение вашей проблемы.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Пример:&lt;/span&gt; требуется &lt;span style="font-weight:bold;"&gt;скачивать видео-ролики с сайта со встроенным плеером&lt;/span&gt;. Плеер самописный, прямой ссылки на проигрываемый файл в коде страницы нет.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Пример страницы:&lt;/span&gt; http://med-edu.ru/basic-science/biohim/shnol1/262. При клике на заставку открывается плеер, в котором можно посмотреть и видео, и слайды презентации:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S6iiDacbhtI/AAAAAAAAAPI/R_pCBiE-Q_4/s1600-h/video-screen.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 229px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S6iiDacbhtI/AAAAAAAAAPI/R_pCBiE-Q_4/s320/video-screen.png" border="0" alt="parsing video"id="BLOGGER_PHOTO_ID_5451785528453859026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В Goodle Chrome находим javascript-функцию, отвечающую за показ видео. Не очень очевидно, не правда ли? :)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function loadVideo() {&lt;br /&gt; &lt;br /&gt;  scroll(0,0);&lt;br /&gt; &lt;br /&gt;  darDiv.id = 'darkDiv';&lt;br /&gt;  newDiv.id = 'divVideo';&lt;br /&gt;  newDivCont.id = 'contVideo';&lt;br /&gt; &lt;br /&gt;  document.body.appendChild(darDiv);&lt;br /&gt;  document.body.appendChild(newDiv);&lt;br /&gt;  newDiv.appendChild(newDivCont);&lt;br /&gt; &lt;br /&gt;  so = new SWFObject("/pl/player7v_n_api_y.swf", "sotester", "1047", "711", "8", "0");&lt;br /&gt;  so.addVariable("xml_php", "/");&lt;br /&gt;  so.addVariable("php_seek", "http://www.medvideo.org/video/flvid/");&lt;br /&gt;  so.addVariable("id", "262");&lt;br /&gt;  so.addParam("scale", "noscale");&lt;br /&gt;  so.addParam("salign", "ct");&lt;br /&gt;  so.addParam("bgcolor", "#333333");&lt;br /&gt;  so.addParam("wmode", "transparent");&lt;br /&gt;  so.addParam("allowFullScreen", "true");&lt;br /&gt;  so.write("contVideo");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;А вот как та же самая функция loadVideo выглядит в никем не любимом IE:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function loadVideo() {&lt;br /&gt;  scroll(0,0);&lt;br /&gt;  $("body").prepend('&amp;lt;div id=&amp;quot;embedplayer&amp;quot; style=&amp;quot;position: absolute; z-index: 99; top: 10px; width: 100%;&amp;quot; align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;&amp;lt;embed width=&amp;quot;1047&amp;quot; height=&amp;quot;711&amp;quot; flashvars=&amp;quot;xml_php=/xml.php?id=262&amp;amp;amp;php_seek=&amp;quot; &lt;br /&gt;allowfullscreen=&amp;quot;true&amp;quot; wmode=&amp;quot;transparent&amp;quot; salign=&amp;quot;ct&amp;quot; scale=&amp;quot;noscale&amp;quot; quality=&amp;quot;high&amp;quot; bgcolor=&amp;quot;#333333&amp;quot; name=&amp;quot;video&amp;quot; id=&amp;quot;123&amp;quot; style=&amp;quot;&amp;quot; &lt;br /&gt;src=&amp;quot;/pl/player7v_n_api_y.swf&amp;quot; type=&amp;quot;application/x-shockwave-flash&amp;quot; base=&amp;quot;/&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;');&lt;br /&gt;  $("body").prepend('&amp;lt;div id=&amp;quot;darklayer&amp;quot; style=&amp;quot;position: absolute; z-index: 98; width: 100%; height: 1300px; &lt;br /&gt;background: none repeat scroll 0 0 #444444; filter:alpha(opacity=60); -moz-opacity: 0.6; opacity: 0.6;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;');&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Итак, стало все намного очевиднее, flashvars открывают завесу тайны :)&lt;br /&gt;&lt;br /&gt;Заходим по ссылке http://med-edu.ru/xml.php?id=262 и видим:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_dS8cdXcqCug/S6iiDroPTgI/AAAAAAAAAPQ/vD3vTlnIsJ8/s1600-h/xml-php.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 275px;" src="http://4.bp.blogspot.com/_dS8cdXcqCug/S6iiDroPTgI/AAAAAAAAAPQ/vD3vTlnIsJ8/s320/xml-php.png" border="0" alt="parsing xml src video"id="BLOGGER_PHOTO_ID_5451785533066792450" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;То есть непосредственно нужный нам файл с видео имеет такой адрес:&lt;br /&gt;http://www.medvideo.org/video/flvid/00000178.flv.&lt;br /&gt;&lt;br /&gt;Ну и, соответственно, для скачивания других роликов с этого сервиса &lt;span style="font-weight:bold;"&gt;алгоритм&lt;/span&gt; определился:&lt;br /&gt;- узнаем id видео (парсингом исходного кода страницы)&lt;br /&gt;- загружаем xml.php?id=...&lt;br /&gt;- парсим полученный xml и находим значение атрибута File.&lt;br /&gt;&lt;br /&gt;Удачи в исследованиях! :)&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons" rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-106708389425436615?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sIagWkvh_4xliHnyseylR-UkzPk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sIagWkvh_4xliHnyseylR-UkzPk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sIagWkvh_4xliHnyseylR-UkzPk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sIagWkvh_4xliHnyseylR-UkzPk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/KdqNjDPwFE8" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/03/parsing-html-in-different-browsers.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S6iiDacbhtI/AAAAAAAAAPI/R_pCBiE-Q_4/s72-c/video-screen.png" height="72" width="72" /><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-6794772341000904896</guid><pubDate>Wed, 10 Mar 2010 13:52:00 +0000</pubDate><atom:updated>2010-06-11T11:00:30.559+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">Synapse</category><category domain="http://www.blogger.com/atom/ns#">способы применения парсинга</category><title>Пример парсинга вопросов с labs.wordtracker.com (Synapse, Delphi)</title><description>Сегодня выкроила время для углубленного изучения &lt;span style="font-weight:bold;"&gt;Synapse&lt;/span&gt;. Только, к сожалению, провела его не очень продуктивно.&lt;br /&gt;&lt;br /&gt;Первым делом посмотрела наработки Сергея (либу &lt;span style="font-weight:bold;"&gt;IngHTTPSend&lt;/span&gt;), предложенные им в комментарии к &lt;a href="http://parsing-and-i.blogspot.com/2010/02/synapse-in-delphi-first-steps.html"&gt;моей первой статье о Synapse&lt;/a&gt;. Безусловно, у него в обертку включены очень полезные функции (в том числе уделено большое внимание кодировкам). Но у каждого программиста есть еще ряд функций, которые он таскает с собой в голове и дописывает к любым своим либам :) Например, у меня есть некоторые заморочки по поводу хэдеров запросов. Я обычно создаю несколько функций для формирования заголовков с одинаковым названием, но с разным набором входных параметров. Например, в функцию может передаваться UserAgent, а может и не передаваться (если не передается, то берется случайный из списка возможных). Обработчики кодов ответа (в частности, 3XX) я тоже обычно выношу.&lt;br /&gt;&lt;br /&gt;А потом пришла к выводу, что ни для чего "глубокомысленного" я сегодня не готова, глянула в списочек ссылок ресурсов, подходящих для демонстрации написания простейших парсеров, и решила поместить в блоге пример с кодом &lt;span style="font-weight:bold;"&gt;парсера вопросов с http://labs.wordtracker.com/keyword-questions/&lt;/span&gt;.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elOOpRERI/AAAAAAAAAO4/9RKM9cMmh_I/s1600-h/question-search.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 223px; height: 320px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elOOpRERI/AAAAAAAAAO4/9RKM9cMmh_I/s320/question-search.gif" border="0" alt="parsing keyword SEO questions from wordtracker" title="parsing keyword SEO questions from wordtracker" id="BLOGGER_PHOTO_ID_5447003938195837202" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ресурс Wordtracker.com я уже брала в качестве "подопытного" для &lt;a href="http://parsing-and-i.blogspot.com/2009/09/curl.html"&gt;иллюстрирования возможностей cURL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Вопросы будем добывать с использованием &lt;span style="font-weight:bold;"&gt;Synapse&lt;/span&gt;.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Исследую запрос на сайте. Как видно, это обычный GET-запрос, с ним никаких сложностей не будет. Ввожу слово и исследую html-код для составления регулярки. Регулярные выражения я всегда тестирую с использованием &lt;a href="http://parsing-and-i.blogspot.com/2008/08/blog-post_25.html"&gt;самописной утилитки&lt;/a&gt;, о которой я уже писала на блоге.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elOdcIGLI/AAAAAAAAAPA/ABO1bd-QbUY/s1600-h/regexp-test.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 271px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elOdcIGLI/AAAAAAAAAPA/ABO1bd-QbUY/s320/regexp-test.gif" border="0" alt="regexp test" title="regexp test" id="BLOGGER_PHOTO_ID_5447003942167255218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Создаю простую форму, на которой Edit1 - для ввода ключевого слова, SG - TStringGrid для вывода результатов.&lt;br /&gt;&lt;br /&gt;Листинг кода получается следующий:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;procedure TMainF.btnGETClick(Sender: TObject);&lt;br /&gt;var&lt;br /&gt;  HTTP    : THTTPSend;&lt;br /&gt;  Res     : boolean;&lt;br /&gt;  S       : string;&lt;br /&gt;  RE      : TRegExp;&lt;br /&gt;  mc      : MatchCollection;&lt;br /&gt;  m       : Match;&lt;br /&gt;  sm      : SubMatches;&lt;br /&gt;  i       : integer;&lt;br /&gt;begin&lt;br /&gt;  HTTP := THTTPSend.Create;&lt;br /&gt;  HTTP.UserAgent:= GetRandomUseragent;&lt;br /&gt;  try&lt;br /&gt;    Res := HTTP.HTTPMethod('GET', 'http://labs.wordtracker.com/keyword-questions/questions?seed='+Edit1.Text+'&amp;commit=Search');&lt;br /&gt;    if Res then&lt;br /&gt;      begin&lt;br /&gt;        case HTTP.Resultcode of&lt;br /&gt;          200 :&lt;br /&gt;            begin&lt;br /&gt;              S := StreamToString(HTTP.Document);&lt;br /&gt;              RE := TRegExp.Create(nil);&lt;br /&gt;              try&lt;br /&gt;                RE.Multiline := true;&lt;br /&gt;                RE.Global := true;&lt;br /&gt;                RE.IgnoreCase := true;&lt;br /&gt;&lt;br /&gt;                RE.Pattern := '&amp;lt;tr.*?&amp;gt;[\x0D\x0A]+&amp;lt;td.*?&amp;gt;[\x0D\x0A]+\d*[\x0D\x0A]+&amp;lt;/td&amp;gt;[\x0D\x0A]+&amp;lt;td&amp;gt;(.*?)&amp;lt;/td&amp;gt;[\x0D\x0A]+&amp;lt;td.*?&amp;gt;(\d*)&amp;lt;/td&amp;gt;[\x0D\x0A]+&amp;lt;/tr&amp;gt;';&lt;br /&gt;                mc := RE.Execute(S) as MatchCollection;&lt;br /&gt;                if mc.Count &gt; 0 then begin&lt;br /&gt;                  for i := 0 to mc.Count - 1 do&lt;br /&gt;                    begin&lt;br /&gt;                      m := mc[i] as Match;&lt;br /&gt;                      sm := m.SubMatches as SubMatches;&lt;br /&gt;&lt;br /&gt;                      SG.RowCount:=SG.RowCount+1;&lt;br /&gt;                      SG.Cells[0,i+1] := VarToStr(sm[0]);&lt;br /&gt;                      SG.Cells[1,i+1] := VarToStr(sm[1]);&lt;br /&gt;                    end;&lt;br /&gt;                end;&lt;br /&gt;              finally&lt;br /&gt;                RE.Free;&lt;br /&gt;              end;&lt;br /&gt;            end;&lt;br /&gt;        end;&lt;br /&gt;      end&lt;br /&gt;  finally&lt;br /&gt;    HTTP.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;где:&lt;br /&gt;&lt;pre&gt;function StreamToString(aStream: TStream): string;&lt;br /&gt;var&lt;br /&gt;  SS: TStringStream;&lt;br /&gt;begin&lt;br /&gt;  if aStream &amp;lt;&amp;gt; nil then&lt;br /&gt;  begin&lt;br /&gt;    SS := TStringStream.Create('');&lt;br /&gt;    try&lt;br /&gt;      aStream.Position := 0;&lt;br /&gt;      SS.CopyFrom(aStream, aStream.Size);&lt;br /&gt;      Result := SS.DataString;&lt;br /&gt;    finally&lt;br /&gt;      SS.Free;&lt;br /&gt;    end;&lt;br /&gt;  end else&lt;br /&gt;  begin&lt;br /&gt;    Result := '';&lt;br /&gt;  end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elN1seBvI/AAAAAAAAAOw/wzPbaQz9EaQ/s1600-h/parsing-result.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 311px;" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elN1seBvI/AAAAAAAAAOw/wzPbaQz9EaQ/s320/parsing-result.gif" border="0" alt="parsing results" id="BLOGGER_PHOTO_ID_5447003931498383090" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Результат представляется в виде конечного списка. Я вывела на форму. Вы можете сохранять в файл или куда угодно.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Кто бы что ни говорил, а составление регулярных выражений захватывает похлеще разгадываний судоку. :)&lt;br /&gt;&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Продолжаю возвращать должки за упоминание RSSAdder-a (несмотря на то, что в последнее время заняться им все еще не получается). Сегодня благодарю за упоминания о нем авторов блогов &lt;a href="http://sibilev.net"&gt;Sibilev.net - Мишкины хроники&lt;/a&gt; и &lt;a href="http://seomuller.ru"&gt;Maximum Seo&lt;/a&gt;.&lt;br /&gt;___&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons" rel="nofollow"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-6794772341000904896?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/E7_3-gEXWMlnC7pdVYjHbt1n52I/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/E7_3-gEXWMlnC7pdVYjHbt1n52I/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/E7_3-gEXWMlnC7pdVYjHbt1n52I/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/E7_3-gEXWMlnC7pdVYjHbt1n52I/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/Et02zAVpdIA" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/03/labswordtrackercom-synapse-delphi.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S5elOOpRERI/AAAAAAAAAO4/9RKM9cMmh_I/s72-c/question-search.gif" height="72" width="72" /><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-3904711634487333761</guid><pubDate>Fri, 19 Feb 2010 10:21:00 +0000</pubDate><atom:updated>2010-02-19T13:35:39.600+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Составление технического задания (ТЗ) на разработку парсера</title><description>Ко мне очень часто обращаются по поводу &lt;span style="font-weight:bold;"&gt;разработки парсеров&lt;/span&gt;. В принципе, я и стремилась именно к этому, когда заводила блог "Парсинг от А до Я". Проанализировав обращения потенциальных заказчиков, я поняла: чтобы облегчить себе жизнь - я должна написать о том, в каком виде хотелось бы получать заказы. Иначе - только четверть обращается с ТЗ, по которому нужны минимальные уточнения (либо даже не нужны совсем). Остальные - с общими фразами "нужно спарсить данные с такого-то сайта. оцените сроки и стоимость, во сколько мне обойдется это, если я закажу парсер вам?". Исходя из чего я должна оценивать сложность и объем работ при такой формулировке?&lt;br /&gt;&lt;br /&gt;Итак, грамотное техническое задание - залог того, что не понадобится недельная переписка, чтобы клещами вытянуть из заказчика описание того, чего именно он хочет. Даже если вы заказываете парсер не у меня :) , ТЗ будет полезно. В первую очередь - вам самим, бо вы поймете и сформулируете в письменном виде свои пожелания.&lt;br /&gt;&lt;br /&gt;Перечислю по пунктам, что должно быть в техническом задании.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;1) Название ресурса, ссылка.&lt;/span&gt; Если необходим парсер только определенного раздела или разделов - указываете здесь же с перечислением всех ссылок (или с перечислением тех ссылок, которые надо исключить).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;2) Данные или инструмент?&lt;/span&gt; Что вы хотите получить в итоге? Может, вам просто единоразово требуется получить данные? Или вам нужна программа/скрипт для того, чтобы самостоятельно получать требующиеся вам данные в любое время?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;3) Если вам нужен "инструмент"&lt;/span&gt;&lt;br /&gt;3.1. Это должен быть скрипт, парсер в виде десктопного приложения или все равно? У каждого вида есть определенные особенности. Скрипты обычно заказывают те, кто хочет запускать их по расписанию на хостинге, чтобы, например, регулярно обновлять данные на своем сайте. Вам подойдет десктопное приложение, если данные, полученные в результате парсинга, будут вами как-нибудь обрабатываться (возможно, автоматизированно) и анализироваться, прежде чем "пойдут" дальше. Причины выбора того или иного исполнения могут быть разными.&lt;br /&gt;&lt;br /&gt;3.2. Входные данные и настройки. Надо ли предусматривать настройку парсера перед запуском и какую именно? Это касается не только регулярных выражений, обычно я их и так выношу в отдельный конфигурационный файл, чтобы при необходимости поменять без перекомпиляции приложения (если это десктопный парсер). Под настройками я в этом пункте имею в виду возможность задавать какие-либо входные параметры. Например, ключевое слово при парсинге выдачи (или список таких слов, который можно загружать из файла). Или выбор категорий, в которых парсить. Настройка задержки в секундах между запросами. И так далее.&lt;br /&gt;&lt;br /&gt;3.3. Нужна ли поддержка работы через прокси-листы? Или какой-нибудь иной метод преодоления защиты от ботов, если таковая имеется на сайте?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;4) Формат данных.&lt;/span&gt;&lt;br /&gt;В каком виде вы хотите получить результирующие данные? Текстовый файл или база данных? У вас разработана структура базы (в этом случае следует ее приложить) или вы хотели бы, чтобы я сама ее разработала? Когда дело доходит до обсуждения этого пункта, я обычно прошу прислать скрины страниц ресурса-"донора" с выделенными (или каким-то другим образом помеченными) в графическом редакторе блоками. Это еще и играет роль гаранта, что клиент потом не скажет, что имел в виду другое и что мы друг друга не поняли. Если требуемая информация состоит из перечня "параметр - значение" (в большинстве интернет-магазинов), то следует оговорить, надо ли каким-то образом разбивать ее на поля или "выдирать" одним целым, прямо с версткой (например, в виде таблицы). Естественно, что от всего этого напрямую будет зависеть цена.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;5) Оплата.&lt;/span&gt;&lt;br /&gt;Для кого-то это очень щекотливая тема. Ничего щекотливого в ней не вижу. Свое рабочее время оцениваю 10$/час, прикидываю, сколько потрачу "чистого" времени, определяю сумму, потом по времени буру период с небольшим запасом, чтобы можно было потестировать. Если требуются только данные - пишу парсер и запускаю. Данные предоставляю после отработки программы (когда сайты большие - заранее время определить не могу, тут уж как все сложится - так сложится). &lt;span style="font-weight:bold;"&gt;Предоплата - 50%.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Если у вас ограниченный бюджет - так прямо и скажите. Лучше не юлить и не спрашивать после оглашения цены, нельзя ли сделать за вот столько-то. Иногда я соглашаюсь на заказы, когда клиент прямо оглашает цену, даже если цифра меньше той, за которую я обычно выполняю проекты аналогичной сложности. Для иллюстрации этого пункта как нельзя лучше подойдет вот этот ролик (я его просто обожаю - настолько там четко все подмечено):&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/oxUbXgGnSyA&amp;hl=ru_RU&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/oxUbXgGnSyA&amp;hl=ru_RU&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;И напоследок еще одна просьба :) Пожалуйста, не напускайте на ваши объяснения таинственности и загадочности. Не бойтесь подробно описывать ваши идеи и требования, уверяю вас, они мне не нужны, у меня самой столько идей, что на их реализацию потребовалось бы не одно десятилетие. Чем подробнее вы все опишете, тем больше шансов, что наше сотрудничество (если такому суждено состояться) будет плодотворным и обоюдоприятным. :) Со своей стороны даю гарантию, что ваши идеи за пределы работы над проектом по вашему заказу не выйдут.&lt;br /&gt;&lt;br /&gt;Желаю удачи всем заказчикам! Ну и фрилансерам заодним :))&lt;br /&gt;____&lt;br /&gt;&lt;br /&gt;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"&gt;подписаться на RSS&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://odnaknopka.ru/ok3.js" type="text/javascript"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-3904711634487333761?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EcauhbvEyijcwlQuEV-Rthu81cA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EcauhbvEyijcwlQuEV-Rthu81cA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EcauhbvEyijcwlQuEV-Rthu81cA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EcauhbvEyijcwlQuEV-Rthu81cA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/xhzMR3ftxQQ" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/02/tehnicheskoe-zadanie-na-parser.html</link><author>noreply@blogger.com (Masha)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3533262157176083768.post-864532178484940883</guid><pubDate>Thu, 04 Feb 2010 13:43:00 +0000</pubDate><atom:updated>2010-06-11T10:57:41.455+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Delphi</category><category domain="http://www.blogger.com/atom/ns#">Synapse</category><category domain="http://www.blogger.com/atom/ns#">компоненты и библиотеки Delphi</category><title>Работа с библиотекой Synapse в Delphi - начало</title><description>Обычно для работы с инетом в своих приложениях я использую компоненты Indy или сокеты. И вот решила, что неплохо было бы расширить свой кругозор и познакомиться с другими библиотеками (и будет вдвойне приятно, если это знакомство окажется полезным).&lt;br /&gt;&lt;br /&gt;Первое, что я решила опробовать, это библиотека &lt;a href="http://synapse.ararat.cz/doku.php"&gt;&lt;b&gt;Synapse&lt;/b&gt;&lt;/a&gt;. Я уже не раз встречала упоминания о ней, но все как-то не доводилось поработать.&lt;br /&gt;&lt;br /&gt;Скачиваем, устанавливаем.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Установка Synapse&lt;/h2&gt;(в моем случае на старенький Delphi 7).&lt;br /&gt;&lt;br /&gt;1. Распаковать архив в какую-нибудь папку (в Source, например)&lt;br /&gt;2. В меню идем Tools - Environment Options - Library и добавляем в Library Path путь к папке ..Source\SYNAPSE\LIB&lt;br /&gt;3. В новосозданном проекте добавляем в Uses-ы httpsend и synacode (при надобности).&lt;br /&gt;&lt;br /&gt;Пришло время "HelloWorld"-а.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Простые примеры кода с использованием Synapse&lt;/h2&gt;&lt;br /&gt;Давайте начнем с самых простых вещей - с &lt;b&gt;получения содержимого страницы&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Поместим на форму кнопку и TMemo, добавим в uses &lt;b&gt;httpsend&lt;/b&gt;, напишем обработчик нажатия кнопки:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;procedure TMainF.Button1Click(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt;  if not HttpGetText('http://parsing-and-i.blogspot.com', Memo1.Lines) then&lt;br /&gt;    ShowMessage('Что-то не получилось.');&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;После нажатия на кнопку в мемо появилось содержимое страниц. Старт взят: с помощью &lt;b&gt;HttpGetText&lt;/b&gt; задачу выполнили.&lt;br /&gt;&lt;br /&gt;Дальнейшее изучение библиотеки можно проводить так — заглянуть непосредственно в файл библиотеки (httpsend.pas). Главным классом является &lt;b&gt;THTTPSend&lt;/b&gt;. Даже просто изучив его структуру, можно понять принципы работы и возможности.&lt;br /&gt;&lt;br /&gt;Итак, &lt;b&gt;HTTPSend.Document&lt;/b&gt; возвращает содержимое всего документа. Поэкспериментируем с использованием еще каких-нибудь элементарных функций. Например, получим заголовок страницы и отдельно код ответа.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;procedure TMainF.Button2Click(Sender: TObject);&lt;br /&gt;var&lt;br /&gt;  HTTP: THTTPSend;&lt;br /&gt;  Res : boolean;&lt;br /&gt;begin&lt;br /&gt;  HTTP := THTTPSend.Create;&lt;br /&gt;  try&lt;br /&gt;    Res := HTTP.HTTPMethod('GET', Edit1.Text);&lt;br /&gt;    if Res then&lt;br /&gt;      begin&lt;br /&gt;        Memo1.Lines.AddStrings(HTTP.Headers);&lt;br /&gt;        Edit2.Text := IntToStr(HTTP.ResultCode);&lt;br /&gt;      end&lt;br /&gt;  finally&lt;br /&gt;    HTTP.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_dS8cdXcqCug/S2rMV5Jbs1I/AAAAAAAAAOk/q7EaXUwOxlI/s1600-h/synapse_get_header_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="synapse headers and resultcode" border="0" height="253" src="http://3.bp.blogspot.com/_dS8cdXcqCug/S2rMV5Jbs1I/AAAAAAAAAOk/q7EaXUwOxlI/s320/synapse_get_header_1.png" title="synapse headers and resultcode" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Задание было несложным. А сейчас попробуем в строке для ввода url набрать "http://yandex.ru". Как известно, если адрес яндекса набирать без "www", то перманентно будет произведен редирект на адрес с "www".&lt;br /&gt;&lt;br /&gt;В indy есть удобное свойство. Там если&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;idHTTP.HandleRedirects := true;&lt;/pre&gt;&lt;br /&gt;то все редиректы будут обрабатываться автоматически (кто работает с php - в cURL такой параметр тоже есть). А здесь мы наблюдаем:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_dS8cdXcqCug/S2rMVkKMhaI/AAAAAAAAAOg/s4lWsaIuqIU/s1600-h/synapse_HttpGetText_301_yan.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="synapse redirect code 301" border="0" height="253" src="http://2.bp.blogspot.com/_dS8cdXcqCug/S2rMVkKMhaI/AAAAAAAAAOg/s4lWsaIuqIU/s320/synapse_HttpGetText_301_yan.png" title="synapse redirect code 301" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;И никакого HandleRedirects.&lt;br /&gt;&lt;br /&gt;Пробуем самостоятельно обработать ситуацию, когда код ответа совпадет с кодом редиректа. Знаем, что в заголовке есть параметр &lt;b&gt;Location&lt;/b&gt;, в котором будет указано новое местоположение страницы. Определим новый location, распарсив заголовок.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;procedure TMainF.Button2Click(Sender: TObject);&lt;br /&gt;var&lt;br /&gt;  HTTP    : THTTPSend;&lt;br /&gt;  Res     : boolean;&lt;br /&gt;  n       : integer;&lt;br /&gt;  NewUrl  : string;&lt;br /&gt;begin&lt;br /&gt;  HTTP := THTTPSend.Create;&lt;br /&gt;  // HTTP.UserAgent:= GetRandomUseragent;&lt;br /&gt;  try&lt;br /&gt;    Res := HTTP.HTTPMethod('GET', Edit1.Text);&lt;br /&gt;    if Res then&lt;br /&gt;      begin&lt;br /&gt;        Memo1.Lines.AddStrings(HTTP.Headers);&lt;br /&gt;        Edit2.Text := IntToStr(HTTP.ResultCode);&lt;br /&gt;        // если код ответа 3XX (т.е. есть редирект)&lt;br /&gt;        case HTTP.Resultcode of&lt;br /&gt;          301, 302, 307:&lt;br /&gt;            begin&lt;br /&gt;              n := FoundLocationStrNum(HTTP.Headers);&lt;br /&gt;              if (n &amp;gt;= 0) and (n &amp;lt;= HTTP.Headers.count) then // если в хэдере найдена строка с локейшеном&lt;br /&gt;                begin&lt;br /&gt;                  NewUrl := StringReplace(HTTP.Headers.Strings[n],'Location: ','',[]);&lt;br /&gt;                  Edit3.Text := NewURL;&lt;br /&gt;                end&lt;br /&gt;              else&lt;br /&gt;                ShowMessage('В заголовке не найдена ссылка для редиректа');&lt;br /&gt;            end;&lt;br /&gt;        end;&lt;br /&gt;      end&lt;br /&gt;  finally&lt;br /&gt;    HTTP.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;где  &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function FoundLocationStrNum(Headers: TStringlist): integer;&lt;br /&gt;var&lt;br /&gt;  FoundStrPos, i   : integer;&lt;br /&gt;begin&lt;br /&gt;  Result:= -1;&lt;br /&gt;  // ищем в заголовке строку, начинающуюся с "Location: "&lt;br /&gt;  for i := 0 to Headers.Count do&lt;br /&gt;  begin&lt;br /&gt;    FoundStrPos := Pos('Location: ', Headers.Strings[i]);&lt;br /&gt;    if FoundStrPos &amp;gt; 0 then&lt;br /&gt;    begin&lt;br /&gt;      Result:= i; // номер строки, в которой указан Location&lt;br /&gt;      exit;&lt;br /&gt;    end;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;По идее, в том месте, где я для примера вывела в edit3 новый URL, надо снова запрашивать страницу, проверять хэдер и так далее (и все это желательно с учетом максимального количества редиректов). Для этого понадобится написание рекурсивной функции или применение готовых "оберток". Наверняка в сети есть готовые библиотеки для облегчения использования Synapse. Попытаюсь к следующему разу собрать какой-нибудь материал и привести пример работы, сейчас уже не хочется с этим возиться. Главное, что сегодня произошло знакомство с библиотекой и базовыми принципами работы с ней.  &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_dS8cdXcqCug/S2rMV2Bv2AI/AAAAAAAAAOo/w9UmMLLGTlk/s1600-h/synapse_get_redirect_url.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="synapse get redirect location code 301" border="0" height="253" src="http://1.bp.blogspot.com/_dS8cdXcqCug/S2rMV2Bv2AI/AAAAAAAAAOo/w9UmMLLGTlk/s320/synapse_get_redirect_url.png" title="synapse get redirect location code 301" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Если вы знаете какие-нибудь удобные библиотеки для работы с Synapse — буду рада ссылкам на них в комментариях.&lt;br /&gt;&lt;br /&gt;____&lt;br /&gt;&lt;br /&gt;&amp;nbsp;Чтобы быть в курсе обновлений блога, можно &lt;a href="http://feeds2.feedburner.com/parsing-lessons"&gt;подписаться на RSS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3533262157176083768-864532178484940883?l=parsing-and-i.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FcS2Cv-nLv2o1NzK4rx9Yyx2Vbw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FcS2Cv-nLv2o1NzK4rx9Yyx2Vbw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FcS2Cv-nLv2o1NzK4rx9Yyx2Vbw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FcS2Cv-nLv2o1NzK4rx9Yyx2Vbw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/parsing-lessons/~4/_fDIk6bccEQ" height="1" width="1"/&gt;</description><link>http://parsing-and-i.blogspot.com/2010/02/synapse-in-delphi-first-steps.html</link><author>noreply@blogger.com (Masha)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_dS8cdXcqCug/S2rMV5Jbs1I/AAAAAAAAAOk/q7EaXUwOxlI/s72-c/synapse_get_header_1.png" height="72" width="72" /><thr:total>21</thr:total></item></channel></rss>

