co.de.mon.key

Weblog

Octopress zamiast Wordpressa - statyczna generacja bloga

| Comments

Obecnie rozpoczęcie blogowania nie nastręcza wielu trudności – trzeba zdecydować się na jedną z platform blogowych, jak Blogger czy Wordpress.com, poświęcić chwilkę na konfigurację oraz wybranie skórki i można zacząć pisać. To wystarcza w zupełności do dzielenia się ze społecznością internetową swoimi przemyśleniami, nawet zespołowo.

W przypadku gdy możliwości platformy przestają wystarczać, reklamy dokuczać, zasięg i poczytność bloga zwiększają się czy w planach jest komercyjne wykorzystanie tworzonych treści, wymagające odpowiedniego zastosowania SEO czy statystyk użytkowników piszący zazwyczaj decyduje się na przejście “na swoje”: wykupienie domeny, hostingu z PHP i MySQL oraz postawieniu na nim silnika – tu nikogo nie zaskoczę – Wordrpress.org.

Wordpress

Standardowa zautomatyzowana instalacja: “ZACZYNAMY”, baza nazwa, hasło, użytkownik, prefiks, DALEJ, tytuł witryny, użytkownik, hasło, mail, DALEJ, logowanie … i co, to już? Gdzie tu zabawa, gdzie konfiguracja, ile razy można? ;–)

Przy wyborze swojego miejsca do notowania zacząłem oczywiście od wyszukania rozwiązań alternatywnych i niebanalnych, co pozwoliło mi trafić na zupełnie inne podejście do generowania blogów.

Zacznijmy może od pytania:

Z czego składa się blog?

Pytanie proste, odpowiedź raczej też, ale ma znaczenie przy dalszych rozważaniach. Przy opisie będę się opierał na WP.

  1. Wpisy
    • tytuł
    • data wpisu (na jej podstawie tworzone jest archiwum / kalendarium bloga)
    • treść (tekst, obrazki)
    • kategoria (przekłada się na listę kategorii z odnośnikami do wpisów)
    • tagi (j.w.)
  2. Strony
  3. Komentarze do wpisów
  4. Menu
  5. Motyw wyglądu
  6. Inne treści – widgety
  7. Panel konfiguracyjny

Pogrubieniem zaznaczyłem pozycje istotne z punktu widzenia samej treści bloga.

Biorąc teraz pod uwagę, że Wordpress to skrypty PHP i leżąca pod spodem baza danych można zadać drugie pytanie:

Jak działa Wordpress?

Jeden prosty GET / HTTP/1.0 do pojedynczego wpisu na blogu opartym na WP to szereg zdarzeń na hostingu, będących w rzeczywistości wykonaniem skryptów PHP, pobieraniem danych z MySQLa oraz prezentacji wyników jako stronę WWW.

  • wczytanie konfiguracji
  • połączenie PHP do bazy z zapytaniem o strukturę Menu i wyświetlenie jej
  • pobranie z MySQLa listy zazwyczaj 3-5+ wpisów, ucinanie do <!-- more -->
  • pobranie listy kategorii z countami po liczbie wpisów,
  • PHP –> MySQL –> select * from wp_comments where comment_post_ID = ...
  • … itd

Sporo kodu, zapytań bazodanowych jedynie po to by pobrać statyczną treść jednego wpisu, trochę więcej zmieniające się komentarze i kilku informacji dodatkowych.

A przecież zawartość tych struktur, oraz strony, na jakie mają wpływ, zmienia się tylko podczas dodawania wpisu, a komentarze przeważnie w pewnym zakresie czasu od daty publikacji. Dodatkowo sporo osób decyduje się na wprowadzenie Disqus czy FB jako osobnego silnika komentarzy.

Dla bloga, na którym wpis pojawia się raz dziennie, oraz który odwiedza w ciągu tego jednego dnia 100 użytkowników to aż 99 niepotrzebnych przegenerowań stron na dzień.

Przyspieszanie Wordpressa – unikanie zbędnej generacji stron

Oczywiście WP jako dojrzała platforma blogowa, która obsługuje w niektórych przypadkach pewnie i setki tysięcy żądań na dzień posiada zalecenia dotyczące wydajności, które zawierają m.in.

  • instalację pluginów cache’ujących – W3 Total Cache czy WP Super Cache
  • cache w przeglądarkach klientów, przydatne dla powracających (HTTP Cache-Control)
  • cache po stronie serwera: Varnish, Reverse proxy, Alternative PHP Cache (APC)

jednak rozwiązania takie dalej bazują na PHP i danych w bazie i są zależne od wydajności hostingu.

Alternatywa – statyczne generowanie stron

Konkurencyjny pomysł jest prosty – zamiast ciągłego przetwarzania PHP + MySQL można przegenerować bloga lub inną stronę w trakcie dodawania treści. Silnik generacji, uzupełniony o szablony struktury , wyglądu (CSS), zapisana w umówionym formacie treść oraz komentarze w zewnętrznym serwisie typu Disqus to wszystko co trzeba aby stworzyć w pełni funkcjonalnego bloga. Unikamy oczywiście drastycznego pomysłu ręcznego dodawania strony html ;)

Jekyll

Przykładem tego podejścia jest Jekyll, napisany w ruby zestaw generatorów oraz skryptów wykonujących całą brudną robotę.

Cechy generatora to, cytując stronę:

  • prostota – “bez baz danych, moderacji komentarzy ani nieznośnych aktualizacji – tylko Twoja treść”
  • statyczność – Markdown (lub Textile) jako format wpisów, Liquid jako silnik szablonów, HTML & CSS. Wyjściem generacji są statyczne witryny gotowe do publikacji.”
  • przygotowany pod blogi – “permalinki, kategorie, strony, wpisy, własne motywy.”

Projekt jest bardzo dobrze udokumentowany, strona prezentuje się bardzo przyjemnie i tłumaczy krok po kroku w instalację oraz sposób publikacji treści.

Quickstart jest imponująco krótki:

1
2
3
4
5
~ $ gem install jekyll
~ $ jekyll new myblog
~ $ cd myblog
~/myblog $ jekyll serve
# => Now browse to http://localhost:4000

i już można podziwiać działającą stronę.

To oczywiście dopiero początek, pozostała praca przy szablonie wizualnym bloga w Liquid.

Same posty to pliki w katalogu _posts o nazwie w postaci YEAR-MONTH-DAY-title.MARKUP, np:

1
2
2011-12-31-new-years-eve-is-awesome.md
2012-09-12-how-to-write-a-blog.textile

Tekst piszemy w Markdown lub Textile, dokładając na początku pliku YAML-owy nagłówek zawierający np. kategorie, tagi i inne informacje dodatkowe. Zawsze można dorzucić fragment HTMLa.

Jeszcze szybszy start – Octopress

Aby jeszcze bardziej ułatwić tworzenie nowego bloga na bazie Jekylla powstał framework zawierający gotową konfigurację, skrypty, szablony wizualne i kod w Javascripcie – Octopress

Out of the box mamy:

  • Gotowy szablon HTML5,
  • Zorientowany na platformy mobilne “responsywny” layout,
  • Wbudowane wsparcie dla Twittera, Google Plus One, komentarzy Disqus, Pinboarda, Delicious, oraz Google Analytics,
  • Łatwe wdrażanie przy użyciu Github pages lub Rsync,
  • Wsparcie dla serwerów POW and Rack dla podglądu wyników pracy,
  • Proste szablony wizualne – Compass i Sass
  • Podświetlanie składni kodu – Solarized

Dodatkowo sporo wtyczek zewnętrznych oraz aktywność społeczności.

Po instalacji:

1
2
3
4
5
6
$ git clone git://github.com/imathis/octopress.git octopress
$ cd octopress
# gem install bundler
# rbenv rehash # If you use rbenv, rehash to be able to run the bundle command
# bundle install 
$ rake install

konfigurujemy deployment na Githubie, Heroku lub rsync-em, główne parametry bloga jak tytuł, tagi meta, rss itp. oraz dodajemy treść:

1
2
3
$ rake new_post["Octopress zamiast Wordpressa"]
mkdir -p source/_posts
Creating new post: source/_posts/2014-02-11-octopress-zamiast-wordpressa.markdown
1
2
3
4
5
6
7
8
9
10
11
12
---
layout: post
title: "Octopress zamiast Wordpressa - statyczna generacja bloga"
date: 2014-02-11 21:20
comments: true
categories: Octopress
---
# Octopress zamiast Wordpressa - statyczna generacja bloga 

## Blogowanie dziś  

Obecnie rozpoczęcie blogowania nie nastręcza wielu trudności ...

Generujemy stronę oraz uruchamiamy serwer testowy, domyślnie na porcie 4000

1
$ rake generate && rake preview

Ostatecznie publikujemy wcześniej skonfigurowanym mechanizmem:

1
$ rake deploy

Polecam zapoznanie się z wygenerowaną strukturą stron na https://github.com/malpka/malpka.github.io

Alternatywy

Statyczne generatory stron to temat rozległy, wystarczy spojrzeć na stronę http://staticsitegenerators.net/, gdzie obecnie jest 224 wpisów. Ciągle pojawiają się nowe, jak np http://jekyllbootstrap.com v0.2 na który trafiłem podczas pisania.

Podsumowanie

Zalety

  • statyczne generowanie stron – przeważnie raz na wpis → szybkość działania,
  • posiada wszystkie ważne funkcje bloga: wpisy, strony, komentarze, tagi, kategorie, permalinki
  • nie wymaga hostingu z PHP i MySQL,
  • wbudowane publikowanie np. na darmowych github pages, do których można podłączyć swoją domenę,
  • uniezależnienie się od Wordpressa (bo ileż można?),
  • na prawdę proste zamieszczanie kodu źródłowego który dodatkowo ładnie wygląda,
  • szybkie pisanie postów na konsoli: rake new_post["tytuł wpisu"] + treść w Markdown/Textile
  • możliwość automatyzacji – np. posty przez maila, posty na bazie innej strony.

Wady

  • wymagana większa wiedza techniczna (ale w końcu to “blogging framework for hackers.”),
  • konieczność przygotowania środowiska w ruby dla samego generatora,
  • konieczność stworzenia własnego szablonu, dostępnych jest kilkadziesiąt(set), w porównaniu do kilku(dziesięciu) tysięcy na WP
  • nie ma wygodnego panelu administracyjnego, posty edytujemy jako pliki
  • brak wygodnego zamieszczania zdjęć, trzeba dodać link bezpośredni
  • odrębny mechanizm komentarzy – Disqus

Usuwanie danych z tabeli w nHibernate

| Comments

Najbardziej generalny sposób, na jaki trafiłem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void CleanUpTable<T>(ISessionFactory sessionFactory)
{
    var metadata = sessionFactory.GetClassMetadata(typeof(T)) as NHibernate.Persister.Entity.AbstractEntityPersister;
    string table = metadata.TableName;

    using (ISession session = sessionFactory.OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            string deleteAll = string.Format("DELETE FROM \"{0}\"", table);
            session.CreateSQLQuery(deleteAll).ExecuteUpdate();

            transaction.Commit();
        }
    }
}

Sposób użycia:

1
CleanUpTable<Person>(sessionFactory);

Źródło

Full text serch na całej bazie MS SQL

| Comments

Do wyszukiwania ciągu znaków we wszystkich polach tekstowych w każdej tabeli w bazie danych posłuży nam ten fragment kodu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
CREATE PROC SearchAllTables
(
    @SearchStr nvarchar(100)
)
AS
BEGIN

    -- Copyright © 2002 Narayana Vyas Kondreddi. All rights reserved.
    -- Purpose: To search all columns of all tables for a given search string
    -- Written by: Narayana Vyas Kondreddi
    -- Site: http://vyaskn.tripod.com
    -- Tested on: SQL Server 7.0 and SQL Server 2000
    -- Date modified: 28th July 2002 22:50 GMT


    CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))

    SET NOCOUNT ON

    DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
    SET  @TableName = ''
    SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

    WHILE @TableName IS NOT NULL
    BEGIN
  SET @ColumnName = ''
  SET @TableName =
  (
      SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
      FROM     INFORMATION_SCHEMA.TABLES
      WHERE        TABLE_TYPE = 'BASE TABLE'
      AND  QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
      AND  OBJECTPROPERTY(
          OBJECT_ID(
              QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
               ), 'IsMSShipped'
                 ) = 0
  )

  WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)
  BEGIN
      SET @ColumnName =
      (
      SELECT MIN(QUOTENAME(COLUMN_NAME))
      FROM     INFORMATION_SCHEMA.COLUMNS
      WHERE        TABLE_SCHEMA = PARSENAME(@TableName, 2)
          AND  TABLE_NAME   = PARSENAME(@TableName, 1)
          AND  DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
          AND  QUOTENAME(COLUMN_NAME) > @ColumnName
      )

      IF @ColumnName IS NOT NULL
      BEGIN
      INSERT INTO #Results
      EXEC
      (
          'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
         FROM ' + @TableName + ' (NOLOCK) ' +
          ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
      )
      END
  END  
    END

    SELECT ColumnName, ColumnValue FROM #Results
END

Wywołanie:

1
2
EXEC SearchAllTables 'Computer'
GO

Źródło: http://vyaskn.tripod.com/search_all_columns_in_all_tables.htm

Tworzenie schematu XSD z XML

| Comments

Przy okazji migracji danych z Joggera na Octopress potrzebowałem szybko dostać schemat struktury całego XML’a eksportu, aby się upewnić, czy w XML’u nie czekają na mnie żadne niespodzianki. Są dostępne różne narzędzia, łącznie z tymi online, ale okazało się że szybko da się to wykonać z wykorzystaniem klasy XmlSchemaInference z .NET:

Pobieranie XSD z XML w C#
1
2
3
4
5
6
7
8
9
XmlReader reader = XmlReader.Create("jogger_eksport.xml");
XmlSchemaSet schemaSet = new XmlSchemaSet();
XmlSchemaInference schema = new XmlSchemaInference();

schemaSet = schema.InferSchema(reader);

using (TextWriter writer = File.CreateText("jogger_eksport.xsd"))
  foreach (XmlSchema s in schemaSet.Schemas())
    s.Write(writer);

otrzymałem XSD, oczywiście bardzo ogólny, joggerowego eksportu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="windows-1250"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="jogger">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="user">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="jid" type="xs:string" />
              <xs:element name="domain" type="xs:string" />
              <xs:element name="alias" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element maxOccurs="unbounded" name="entry">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="date" type="xs:string" />
              <xs:element name="jid" type="xs:string" />
              <xs:element name="level_id" type="xs:unsignedByte" />
              <xs:element name="comment_mode" type="xs:unsignedByte" />
              <xs:element name="subject" type="xs:string" />
              <xs:element name="body" type="xs:string" />
              <xs:element name="tags" type="xs:string" />
              <xs:element name="permalink" type="xs:string" />
              <xs:element name="trackback" type="xs:string" />
              <xs:element maxOccurs="unbounded" name="category" type="xs:string" />
              <xs:element minOccurs="0" maxOccurs="unbounded" name="comment">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="date" type="xs:string" />
                    <xs:element name="nick" type="xs:string" />
                    <xs:element name="nick_url" type="xs:string" />
                    <xs:element name="body" type="xs:string" />
                    <xs:element name="ip" type="xs:string" />
                    <xs:element name="trackback" />
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Migracja Jogger - Octopress

| Comments

tl;dr

Skrypt w pythonie 3 przetwarzający plik xml z danymi Joggera na pliki .markdown gotowe do wrzucenia do source/_posts/ w Octopressie

Pobieranie danych z Joggera

Dane z Joggera można pobrać z panelu administracyjnego: Opcje → Eksport → Wygeneruj eksport → Pobierz Są one w formacie XML, spakowane gz. Odpowiednio sformatowane prezentują się jak poniżej:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<jogger>
  <user>
    <jid>malpka@przykladowy.jid</jid>
    <domain>malpka</domain>
    <alias/>
  </user>
  <entry>
    <date>2007-01-11 00:29:07</date>
    <jid>malpka@przykladowy.jid</jid>
    <level_id>1</level_id>
    <comment_mode>0</comment_mode>
    <subject>temat</subject>
    <body>
      &lt;p&gt;tresc tresc tresc&lt;/p&gt;
    </body>
    <tags/>
    <permalink>temat</permalink>
    <trackback/>
    <category>Ogólne</category>
    <category>Inna kategoria</category>
    <comment>
      <date>2007-01-11 03:35:52</date>
      <nick>NickKomentujacy</nick>
      <nick_url>http://google.pl</nick_url>
      <body>&lt;p&gt;*wpadłem bo spać nie mogę :)*&lt;/p&gt;</body>
      <ip>127.0.0.1</ip>
      <trackback/>
    </comment>
  </entry>
    ...
</jogger>

Oczywiście wartość gałęzi body to escapowany html wpisu.

W razie czego można wygenerować sobie XSD z XML.

Przetwarzanie danych eksportu na wpisy Octopressa

Skoro już wiadomo co i jak, można pokusić się o wyciągnięcie podstawowych informacji z XML’a: daty, tytułu, permalinka (który potraktuję jako część nazwy pliku), kategorii i treści wpisu. Przy okazji wyszło, że Jogger pozwalał na ustawienie pustego tytułu wpisu, co skutkuje brakiem wartości taga <subject/>

Tym razem python 3.

Sposób użycia:

  • rozpakować plik z danymi eksportu jako jogger_eksport.xml, najlepiej do osobnego katalogu
  • umieścić skrypt razem z plikiem xml
  • uruchomić skrypt
  • w katalogu dla każdego wpisu powstanie osobny plik .markdown, nazwany zgodnie z regułami Octopressa, z uzupełnionymi polami: tytuł, data, kategorie, oraz z treścią posta wpisaną w HTML’u

TODO:

  • zachowanie treści szkiców (to już trzeba ręcznie)
  • zmiana treści na markdown

Hello World

| Comments

Z nowym rokiem wdrażam w życie plan prowadzenia notatnika online, zawierającego przemyślenia, doświadczenia, zmagania z – najczęściej okołoprogramistyczną – rzeczywistością. Tematów raczej nie brak, nawet sporo czeka w kolejce na opublikowanie. Jedynie wolny czas będzie ograniczeniem ;–)

Dodatkowo będzie to miejsce na najciekawsze znaleziska internetowe – zabawne, praktyczne, intrygujące – takie, do których warto / chciałoby się wrócić w przyszłości a nie ma tego gdzie trzymać (obecnie lista zakładek w przeglądarce ma około 880 pozycji, boję się tam zaglądać!).

W planach jest zapoznanie się z Markdown’em, dodanie disqus’a, lekka modyfikacja skórki, rozeznanie sie we wnętrznościach Octopressa, możliwe że powiązanie domeny z github pages czy import niektórych wpisów z Joggera

1 liner - pobieranie wersji .NET framework dla bibliotek w katalogu

| Comments

Przydatne, gdy trzeba określić ostateczną wersję frameworka, np do wymagań instalatora.

for %p in (*.dll) do @echo %p && @ildasm.exe %p /metadata[=MDHEADER] /text /noil | grep -E -o "version: v[a-z0-9\.]+"

Nie ma grepa na Windowsie? Jak tak można w ogóle pracować… ;)

http://gnuwin32.sourceforge.net/packages/grep.htm

Przykładowe wyjście:

Microsoft.Practices.CompositeUI.dll
version: v2.0.50727
Microsoft.Practices.CompositeUI.WinForms.dll
version: v2.0.50727
Microsoft.Practices.EnterpriseLibrary.Caching.dll
version: v2.0.50727
Microsoft.Practices.EnterpriseLibrary.Common.dll
version: v2.0.50727

Źródło inspiracji

Oczywiście są również wersje okienkowe, jak wspomniany w odpowiedziach dotPeek.

Pyhon - hurtowa zmiana atrybutów w xml

| Comments

Kolejne kilka linijek, które ułatwią mi kiedyś życie.

Czasem zdarza się, że pobieranie wersji pliku dll w C# zwraca śmieci zamiast standardowych A.B.C.D. W moim przypadku biblioteki stworzone chyba w delphi miały zwyczaj stosować przecinki zamiast kropek, albo dodawać spacje między liczbami.

Ponieważ korzystam z narzędzia, które generuje mi do xmla listę plików w katalogu, z uwzględnieniem sumy kontrolnej i wersji, a nie miałem jego źródeł, trzeba było napisać szybką łatkę na wygenerowane dane.

from xml.dom import minidom
DOMTree = minidom.parse('filelist.xml')
cNodes = DOMTree.childNodes
for i in cNodes[0].getElementsByTagName("File"):
        version = i.getAttribute("version")
        version = version.replace(",", ".")
        version = version.replace(" ", "")
        i.setAttribute("version", version)

f = open('filelist.xml', 'w')
DOMTree.writexml(f)
f.close()

Dzień Darmowej Dostawy w Polsce - opisy sklepów.

| Comments

Ostatnio dowiedziałem się o akcji Dzień Darmowej Dostawy, która odbędzie się 1 grudnia 2010 r., wzorowanej na amerykańskim Free Shipping Day. Cel jest oczywisty, a szczegółowe informacje są podane na stronie http://dziendarmowejdostawy.pl/.

Firmy uczestniczące w akcji są wymienione w formie adresów internetowych stron, bez odsyłaczy. Poza bardziej znanymi partnerami akcji jak gram.pl, Vobis, Helion, Komputronik czy Megastore jest sporo sklepów, o których działalności niewiele można powiedzieć po samym adresie internetowym. Parę przykładów - Fnak.pl, DeeZee.pl, Maminek.pl, KissMyKicks.pl czy KolorowaKrowa.pl ! :)

W celu szybkiego sprawdzenia tematyki każdego ze sklepów popełniłem mały skypcik w pythonie, w którym widać łatwość z jaką można wkroczyć w tematykę zwaną Web scraping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib, sys, BeautifulSoup

adresDDD = "http://dziendarmowejdostawy.pl/"
soup = BeautifulSoup.BeautifulSoup(urllib.urlopen(adresDDD))

for li in soup.findAll('li'):
        try:
                a2 = li.contents[0].strip(" \t\n").decode('utf8')
                print "[http://" + a2+ "]"

                soup2 = BeautifulSoup.BeautifulSoup(urllib.urlopen("http://"+a2))
                try:
                        print unicode(soup2.head.title)
                except:
                        print soup2.head.title
        except:
                continue


Na przykładzie widać najprostsze wyciąganie danych za pomocą pythona i biblioteki BeautifulSoup.

Po delikatnym przeformatowaniu w npp można uzyskać coś takiego:


  • http://Gram.pl - gram.pl - Recenzje, Zapowiedzi, Newsy, Poradniki, Dema. Gry na PC, PS3, X360.
  • http://Onepress.pl - Księgarnia biznesowa Onepress.pl - książki klasy business
  • http://Rebel.pl - Sklep z grami REBEL.pl :: Największy polski sklep z grami - gry rpg, gry planszowe, gry karciane i inne
  • http://Pixers.pl - Fototapety, Obrazy na ścianę, Plakaty, Naklejki, Zdjęcia na płótnie - najciekawszy sklep w Europie - PIXERS

Wybór klawiatury

| Comments

Zastanawiam się nad następującymi:

I monitorem

I obudową

A i jeszcze kartą graficzną:

  • ATI Radeon 4850 512Mb ddr3
  • HiS Radeon HD 4670 512 MB IceQ
  • GeForce 9600 GT

Bo procesor, płytę główną i pamięć już sobie na imieniny sprawiłem ;)

Obecna konfiguracja:

  • AMD Phenom II X2 555 Black Edition, AM3, 3,2 GHz, 7MB , 80W, BOX (HDZ555WFGMBOX) - 399 zł
  • Gigabyte GA-MA785GMT-UD2H - 324 zł
  • Good Ram DDR3 2048MB PC1333 CL9 (GR1333D364L9/2G) - 109 zł
  • Good Ram DDR3 4096MB PC1333 CL9 (GR1333D364L9/4G) - 119 zł (lipiec 2013)
  • WD Caviar GP-16 1TB WD10EADS 32 MB Green Power 295 zł
  • Corsair CMPSU-520HXEU 520W - 239 zł
  • MSI Radeon HD6870 Hawk - 688 zł